@datacore-one/cli 1.0.5 → 1.0.7
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/dist/index.js +2145 -953
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6980,6 +6980,441 @@ var require_dist = __commonJS((exports) => {
|
|
|
6980
6980
|
exports.visitAsync = visit.visitAsync;
|
|
6981
6981
|
});
|
|
6982
6982
|
|
|
6983
|
+
// src/lib/space.ts
|
|
6984
|
+
var exports_space = {};
|
|
6985
|
+
__export(exports_space, {
|
|
6986
|
+
listSpaces: () => listSpaces,
|
|
6987
|
+
getSpace: () => getSpace,
|
|
6988
|
+
getNextSpaceNumber: () => getNextSpaceNumber,
|
|
6989
|
+
createSpace: () => createSpace,
|
|
6990
|
+
auditSpace: () => auditSpace
|
|
6991
|
+
});
|
|
6992
|
+
import { existsSync as existsSync3, readdirSync, statSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
6993
|
+
import { join as join3, basename } from "path";
|
|
6994
|
+
function listSpaces() {
|
|
6995
|
+
if (!existsSync3(DATA_DIR2)) {
|
|
6996
|
+
return [];
|
|
6997
|
+
}
|
|
6998
|
+
const entries = readdirSync(DATA_DIR2, { withFileTypes: true });
|
|
6999
|
+
const spaces = [];
|
|
7000
|
+
for (const entry of entries) {
|
|
7001
|
+
if (!entry.isDirectory())
|
|
7002
|
+
continue;
|
|
7003
|
+
const match = entry.name.match(/^(\d+)-(.+)$/);
|
|
7004
|
+
if (!match)
|
|
7005
|
+
continue;
|
|
7006
|
+
const [, numStr, name] = match;
|
|
7007
|
+
const number = parseInt(numStr, 10);
|
|
7008
|
+
const path = join3(DATA_DIR2, entry.name);
|
|
7009
|
+
spaces.push({
|
|
7010
|
+
name: entry.name,
|
|
7011
|
+
number,
|
|
7012
|
+
path,
|
|
7013
|
+
type: number === 0 ? "personal" : "team",
|
|
7014
|
+
hasGit: existsSync3(join3(path, ".git")),
|
|
7015
|
+
hasClaude: existsSync3(join3(path, "CLAUDE.md")) || existsSync3(join3(path, "CLAUDE.base.md"))
|
|
7016
|
+
});
|
|
7017
|
+
}
|
|
7018
|
+
spaces.sort((a, b) => a.number - b.number);
|
|
7019
|
+
return spaces;
|
|
7020
|
+
}
|
|
7021
|
+
function getSpace(nameOrNumber) {
|
|
7022
|
+
const spaces = listSpaces();
|
|
7023
|
+
if (typeof nameOrNumber === "number") {
|
|
7024
|
+
return spaces.find((s) => s.number === nameOrNumber) || null;
|
|
7025
|
+
}
|
|
7026
|
+
let space = spaces.find((s) => s.name === nameOrNumber);
|
|
7027
|
+
if (space)
|
|
7028
|
+
return space;
|
|
7029
|
+
space = spaces.find((s) => s.name.includes(nameOrNumber));
|
|
7030
|
+
return space || null;
|
|
7031
|
+
}
|
|
7032
|
+
function getNextSpaceNumber() {
|
|
7033
|
+
const spaces = listSpaces();
|
|
7034
|
+
if (spaces.length === 0)
|
|
7035
|
+
return 0;
|
|
7036
|
+
const maxNumber = Math.max(...spaces.map((s) => s.number));
|
|
7037
|
+
return maxNumber + 1;
|
|
7038
|
+
}
|
|
7039
|
+
function createSpace(name, type = "team") {
|
|
7040
|
+
const number = type === "personal" ? 0 : getNextSpaceNumber();
|
|
7041
|
+
const normalizedName = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
7042
|
+
const folderName = `${number}-${normalizedName}`;
|
|
7043
|
+
const spacePath = join3(DATA_DIR2, folderName);
|
|
7044
|
+
if (existsSync3(spacePath)) {
|
|
7045
|
+
throw new Error(`Space already exists: ${folderName}`);
|
|
7046
|
+
}
|
|
7047
|
+
mkdirSync2(spacePath, { recursive: true });
|
|
7048
|
+
const dirs = type === "personal" ? [
|
|
7049
|
+
".datacore",
|
|
7050
|
+
".datacore/commands",
|
|
7051
|
+
".datacore/agents",
|
|
7052
|
+
".datacore/learning",
|
|
7053
|
+
".datacore/state",
|
|
7054
|
+
".datacore/env",
|
|
7055
|
+
"org",
|
|
7056
|
+
"0-inbox",
|
|
7057
|
+
"notes",
|
|
7058
|
+
"notes/journals",
|
|
7059
|
+
"notes/pages",
|
|
7060
|
+
"notes/zettel",
|
|
7061
|
+
"journal",
|
|
7062
|
+
"3-knowledge",
|
|
7063
|
+
"3-knowledge/pages",
|
|
7064
|
+
"3-knowledge/zettel",
|
|
7065
|
+
"3-knowledge/literature",
|
|
7066
|
+
"3-knowledge/reference",
|
|
7067
|
+
"4-archive",
|
|
7068
|
+
"content"
|
|
7069
|
+
] : [
|
|
7070
|
+
".datacore",
|
|
7071
|
+
".datacore/commands",
|
|
7072
|
+
".datacore/agents",
|
|
7073
|
+
".datacore/learning",
|
|
7074
|
+
".datacore/state",
|
|
7075
|
+
".datacore/env",
|
|
7076
|
+
"org",
|
|
7077
|
+
"0-inbox",
|
|
7078
|
+
"journal",
|
|
7079
|
+
"1-tracks",
|
|
7080
|
+
"1-tracks/ops",
|
|
7081
|
+
"1-tracks/product",
|
|
7082
|
+
"1-tracks/dev",
|
|
7083
|
+
"1-tracks/research",
|
|
7084
|
+
"1-tracks/comms",
|
|
7085
|
+
"2-projects",
|
|
7086
|
+
"3-knowledge",
|
|
7087
|
+
"3-knowledge/pages",
|
|
7088
|
+
"3-knowledge/zettel",
|
|
7089
|
+
"3-knowledge/literature",
|
|
7090
|
+
"3-knowledge/reference",
|
|
7091
|
+
"4-archive"
|
|
7092
|
+
];
|
|
7093
|
+
for (const dir of dirs) {
|
|
7094
|
+
mkdirSync2(join3(spacePath, dir), { recursive: true });
|
|
7095
|
+
}
|
|
7096
|
+
if (type === "personal") {
|
|
7097
|
+
writeFileSync2(join3(spacePath, "org", "inbox.org"), `#+TITLE: Inbox
|
|
7098
|
+
#+FILETAGS: :inbox:
|
|
7099
|
+
|
|
7100
|
+
Capture everything here. Process daily to zero.
|
|
7101
|
+
|
|
7102
|
+
* TODO Do more. With less.
|
|
7103
|
+
* Inbox
|
|
7104
|
+
`);
|
|
7105
|
+
writeFileSync2(join3(spacePath, "org", "next_actions.org"), `#+TITLE: Next Actions
|
|
7106
|
+
#+TODO: TODO NEXT WAITING | DONE CANCELED
|
|
7107
|
+
#+FILETAGS: :tasks:
|
|
7108
|
+
|
|
7109
|
+
Tasks organized by focus area. Tag with :AI: to delegate to agents.
|
|
7110
|
+
|
|
7111
|
+
* TIER 1: STRATEGIC FOUNDATION
|
|
7112
|
+
** /Projects
|
|
7113
|
+
** /Work
|
|
7114
|
+
* TIER 2: SUPPORTING WORK
|
|
7115
|
+
** Admin
|
|
7116
|
+
** Maintenance
|
|
7117
|
+
* PERSONAL: LIFE & DEVELOPMENT
|
|
7118
|
+
** /Personal Development
|
|
7119
|
+
** /Health & Longevity
|
|
7120
|
+
** Home & Family
|
|
7121
|
+
** Financial Management
|
|
7122
|
+
* RESEARCH & LEARNING
|
|
7123
|
+
** Technology
|
|
7124
|
+
** Skills
|
|
7125
|
+
`);
|
|
7126
|
+
writeFileSync2(join3(spacePath, "org", "nightshift.org"), `#+TITLE: Nightshift Queue
|
|
7127
|
+
#+TODO: QUEUED EXECUTING | DONE FAILED
|
|
7128
|
+
#+FILETAGS: :nightshift:
|
|
7129
|
+
|
|
7130
|
+
AI tasks queued for overnight execution. Managed by /tomorrow command.
|
|
7131
|
+
|
|
7132
|
+
* Queue
|
|
7133
|
+
`);
|
|
7134
|
+
writeFileSync2(join3(spacePath, "org", "habits.org"), `#+TITLE: Habits
|
|
7135
|
+
#+FILETAGS: :habits:
|
|
7136
|
+
|
|
7137
|
+
Recurring behaviors and routines. Track with org-habit.
|
|
7138
|
+
|
|
7139
|
+
* Daily
|
|
7140
|
+
* Weekly
|
|
7141
|
+
* Monthly
|
|
7142
|
+
`);
|
|
7143
|
+
writeFileSync2(join3(spacePath, "org", "someday.org"), `#+TITLE: Someday/Maybe
|
|
7144
|
+
#+FILETAGS: :someday:
|
|
7145
|
+
|
|
7146
|
+
Ideas and projects for the future. Review monthly.
|
|
7147
|
+
|
|
7148
|
+
* Someday
|
|
7149
|
+
* Maybe
|
|
7150
|
+
`);
|
|
7151
|
+
writeFileSync2(join3(spacePath, "org", "archive.org"), `#+TITLE: Archive
|
|
7152
|
+
#+FILETAGS: :archive:
|
|
7153
|
+
|
|
7154
|
+
Completed and canceled tasks. Searchable history.
|
|
7155
|
+
|
|
7156
|
+
* Archived Tasks
|
|
7157
|
+
`);
|
|
7158
|
+
} else {
|
|
7159
|
+
writeFileSync2(join3(spacePath, "org", "inbox.org"), `#+TITLE: Inbox
|
|
7160
|
+
#+FILETAGS: :inbox:
|
|
7161
|
+
|
|
7162
|
+
* Capture items here
|
|
7163
|
+
`);
|
|
7164
|
+
writeFileSync2(join3(spacePath, "org", "next_actions.org"), `#+TITLE: Next Actions
|
|
7165
|
+
#+FILETAGS: :tasks:
|
|
7166
|
+
|
|
7167
|
+
* Tasks
|
|
7168
|
+
** TODO items go here
|
|
7169
|
+
`);
|
|
7170
|
+
}
|
|
7171
|
+
if (type === "personal") {
|
|
7172
|
+
writeFileSync2(join3(spacePath, "_index.md"), `# Personal Space
|
|
7173
|
+
|
|
7174
|
+
Your personal knowledge base and GTD system.
|
|
7175
|
+
|
|
7176
|
+
## GTD Workflow
|
|
7177
|
+
|
|
7178
|
+
1. **Capture** → \`org/inbox.org\` - dump everything here
|
|
7179
|
+
2. **Clarify** → Is it actionable? What's the next action?
|
|
7180
|
+
3. **Organize** → Move to \`next_actions.org\` by focus area
|
|
7181
|
+
4. **Reflect** → Weekly review, monthly strategic
|
|
7182
|
+
5. **Engage** → Do the work, delegate with :AI: tags
|
|
7183
|
+
|
|
7184
|
+
## Org Files (GTD)
|
|
7185
|
+
|
|
7186
|
+
| File | Purpose |
|
|
7187
|
+
|------|---------|
|
|
7188
|
+
| [inbox.org](org/inbox.org) | Single capture point - process to zero daily |
|
|
7189
|
+
| [next_actions.org](org/next_actions.org) | Active tasks by focus area |
|
|
7190
|
+
| [nightshift.org](org/nightshift.org) | AI task queue (overnight processing) |
|
|
7191
|
+
| [habits.org](org/habits.org) | Recurring behaviors |
|
|
7192
|
+
| [someday.org](org/someday.org) | Future possibilities |
|
|
7193
|
+
| [archive.org](org/archive.org) | Completed/canceled tasks |
|
|
7194
|
+
|
|
7195
|
+
## Knowledge
|
|
7196
|
+
|
|
7197
|
+
| Folder | Purpose |
|
|
7198
|
+
|--------|---------|
|
|
7199
|
+
| [notes/](notes/) | Personal knowledge base (Obsidian) |
|
|
7200
|
+
| [notes/journals/](notes/journals/) | Daily personal journals |
|
|
7201
|
+
| [3-knowledge/](3-knowledge/) | Structured knowledge (zettelkasten) |
|
|
7202
|
+
|
|
7203
|
+
## Other
|
|
7204
|
+
|
|
7205
|
+
- [journal/](journal/) - AI session journals (auto-generated)
|
|
7206
|
+
- [content/](content/) - Generated content (drafts, emails)
|
|
7207
|
+
- [0-inbox/](0-inbox/) - File inbox (process files here)
|
|
7208
|
+
- [4-archive/](4-archive/) - Historical content
|
|
7209
|
+
`);
|
|
7210
|
+
} else {
|
|
7211
|
+
writeFileSync2(join3(spacePath, "_index.md"), `# ${name}
|
|
7212
|
+
|
|
7213
|
+
Team space.
|
|
7214
|
+
|
|
7215
|
+
## Structure
|
|
7216
|
+
|
|
7217
|
+
- \`org/\` - Task coordination
|
|
7218
|
+
- \`1-tracks/\` - Department work
|
|
7219
|
+
- \`2-projects/\` - Code repositories
|
|
7220
|
+
- \`3-knowledge/\` - Shared knowledge
|
|
7221
|
+
- \`4-archive/\` - Historical content
|
|
7222
|
+
|
|
7223
|
+
## Quick Links
|
|
7224
|
+
|
|
7225
|
+
- [Inbox](org/inbox.org)
|
|
7226
|
+
- [Tasks](org/next_actions.org)
|
|
7227
|
+
- [Journal](journal/)
|
|
7228
|
+
- [Knowledge](3-knowledge/)
|
|
7229
|
+
`);
|
|
7230
|
+
}
|
|
7231
|
+
if (type === "personal") {
|
|
7232
|
+
writeFileSync2(join3(spacePath, "CLAUDE.base.md"), `# Personal Space
|
|
7233
|
+
|
|
7234
|
+
Personal GTD system and knowledge base (per DIP-0009).
|
|
7235
|
+
|
|
7236
|
+
## GTD Files
|
|
7237
|
+
|
|
7238
|
+
| File | Purpose | Process |
|
|
7239
|
+
|------|---------|---------|
|
|
7240
|
+
| \`org/inbox.org\` | Single capture point | Process to zero daily |
|
|
7241
|
+
| \`org/next_actions.org\` | Active tasks by focus area | Work from here |
|
|
7242
|
+
| \`org/nightshift.org\` | AI task queue | Auto-managed by /tomorrow |
|
|
7243
|
+
| \`org/habits.org\` | Recurring behaviors | Track daily |
|
|
7244
|
+
| \`org/someday.org\` | Future possibilities | Review monthly |
|
|
7245
|
+
| \`org/archive.org\` | Completed tasks | Searchable history |
|
|
7246
|
+
|
|
7247
|
+
## Task States
|
|
7248
|
+
|
|
7249
|
+
| State | Meaning |
|
|
7250
|
+
|-------|---------|
|
|
7251
|
+
| \`TODO\` | Standard next action |
|
|
7252
|
+
| \`NEXT\` | High priority, work today |
|
|
7253
|
+
| \`WAITING\` | Blocked on external |
|
|
7254
|
+
| \`DONE\` | Completed (terminal) |
|
|
7255
|
+
| \`CANCELED\` | Will not do (terminal) |
|
|
7256
|
+
|
|
7257
|
+
## AI Delegation
|
|
7258
|
+
|
|
7259
|
+
Tag tasks with \`:AI:\` to delegate to agents:
|
|
7260
|
+
|
|
7261
|
+
| Tag | Agent | Autonomous |
|
|
7262
|
+
|-----|-------|------------|
|
|
7263
|
+
| \`:AI:content:\` | gtd-content-writer | Yes |
|
|
7264
|
+
| \`:AI:research:\` | gtd-research-processor | Yes |
|
|
7265
|
+
| \`:AI:data:\` | gtd-data-analyzer | Yes |
|
|
7266
|
+
| \`:AI:pm:\` | gtd-project-manager | Yes |
|
|
7267
|
+
|
|
7268
|
+
## Knowledge Structure
|
|
7269
|
+
|
|
7270
|
+
| Location | Purpose |
|
|
7271
|
+
|----------|---------|
|
|
7272
|
+
| \`notes/\` | Personal knowledge base (Obsidian) |
|
|
7273
|
+
| \`notes/journals/\` | Daily personal reflections |
|
|
7274
|
+
| \`3-knowledge/zettel/\` | Atomic concept notes |
|
|
7275
|
+
| \`3-knowledge/literature/\` | Source summaries |
|
|
7276
|
+
|
|
7277
|
+
## Daily Workflow
|
|
7278
|
+
|
|
7279
|
+
1. **Morning** - Run \`/today\` for briefing
|
|
7280
|
+
2. **Capture** - Everything to inbox.org
|
|
7281
|
+
3. **Process** - Clear inbox, route tasks
|
|
7282
|
+
4. **Work** - Focus on NEXT items
|
|
7283
|
+
5. **Evening** - Run \`/tomorrow\` for wrap-up
|
|
7284
|
+
|
|
7285
|
+
## See Also
|
|
7286
|
+
|
|
7287
|
+
Parent: ~/Data/CLAUDE.md
|
|
7288
|
+
`);
|
|
7289
|
+
} else {
|
|
7290
|
+
writeFileSync2(join3(spacePath, "CLAUDE.base.md"), `# ${name} Space
|
|
7291
|
+
|
|
7292
|
+
Team space for ${name}.
|
|
7293
|
+
|
|
7294
|
+
## Structure
|
|
7295
|
+
|
|
7296
|
+
See parent CLAUDE.md for full documentation.
|
|
7297
|
+
`);
|
|
7298
|
+
}
|
|
7299
|
+
writeFileSync2(join3(spacePath, ".datacore", "config.yaml"), `# Space configuration
|
|
7300
|
+
name: ${normalizedName}
|
|
7301
|
+
type: ${type}
|
|
7302
|
+
`);
|
|
7303
|
+
writeFileSync2(join3(spacePath, ".datacore", "learning", "patterns.md"), `# Patterns
|
|
7304
|
+
|
|
7305
|
+
Successful approaches to remember.
|
|
7306
|
+
`);
|
|
7307
|
+
writeFileSync2(join3(spacePath, ".datacore", "learning", "corrections.md"), `# Corrections
|
|
7308
|
+
|
|
7309
|
+
Human feedback log.
|
|
7310
|
+
`);
|
|
7311
|
+
writeFileSync2(join3(spacePath, ".datacore", "learning", "preferences.md"), `# Preferences
|
|
7312
|
+
|
|
7313
|
+
Style and preference notes.
|
|
7314
|
+
`);
|
|
7315
|
+
writeFileSync2(join3(spacePath, ".gitignore"), type === "personal" ? `.datacore/state/
|
|
7316
|
+
.datacore/env/
|
|
7317
|
+
CLAUDE.md
|
|
7318
|
+
CLAUDE.local.md
|
|
7319
|
+
` : `.datacore/state/
|
|
7320
|
+
.datacore/env/
|
|
7321
|
+
CLAUDE.md
|
|
7322
|
+
CLAUDE.local.md
|
|
7323
|
+
2-projects/
|
|
7324
|
+
`);
|
|
7325
|
+
return {
|
|
7326
|
+
name: folderName,
|
|
7327
|
+
number,
|
|
7328
|
+
path: spacePath,
|
|
7329
|
+
type,
|
|
7330
|
+
hasGit: false,
|
|
7331
|
+
hasClaude: true
|
|
7332
|
+
};
|
|
7333
|
+
}
|
|
7334
|
+
function auditSpace(nameOrPath) {
|
|
7335
|
+
let spacePath;
|
|
7336
|
+
let spaceName;
|
|
7337
|
+
if (existsSync3(nameOrPath) && statSync(nameOrPath).isDirectory()) {
|
|
7338
|
+
spacePath = nameOrPath;
|
|
7339
|
+
spaceName = basename(nameOrPath);
|
|
7340
|
+
} else {
|
|
7341
|
+
const space = getSpace(nameOrPath);
|
|
7342
|
+
if (!space) {
|
|
7343
|
+
throw new Error(`Space not found: ${nameOrPath}`);
|
|
7344
|
+
}
|
|
7345
|
+
spacePath = space.path;
|
|
7346
|
+
spaceName = space.name;
|
|
7347
|
+
}
|
|
7348
|
+
const issues = [];
|
|
7349
|
+
const requiredDirs = [
|
|
7350
|
+
".datacore",
|
|
7351
|
+
"org",
|
|
7352
|
+
"0-inbox",
|
|
7353
|
+
"journal"
|
|
7354
|
+
];
|
|
7355
|
+
for (const dir of requiredDirs) {
|
|
7356
|
+
if (!existsSync3(join3(spacePath, dir))) {
|
|
7357
|
+
issues.push({
|
|
7358
|
+
type: "missing",
|
|
7359
|
+
path: dir,
|
|
7360
|
+
message: `Required directory missing: ${dir}`
|
|
7361
|
+
});
|
|
7362
|
+
}
|
|
7363
|
+
}
|
|
7364
|
+
const requiredFiles = [
|
|
7365
|
+
"org/inbox.org",
|
|
7366
|
+
"org/next_actions.org"
|
|
7367
|
+
];
|
|
7368
|
+
for (const file of requiredFiles) {
|
|
7369
|
+
if (!existsSync3(join3(spacePath, file))) {
|
|
7370
|
+
issues.push({
|
|
7371
|
+
type: "missing",
|
|
7372
|
+
path: file,
|
|
7373
|
+
message: `Required file missing: ${file}`
|
|
7374
|
+
});
|
|
7375
|
+
}
|
|
7376
|
+
}
|
|
7377
|
+
const recommendedDirs = [
|
|
7378
|
+
"1-tracks",
|
|
7379
|
+
"2-projects",
|
|
7380
|
+
"3-knowledge",
|
|
7381
|
+
"4-archive",
|
|
7382
|
+
".datacore/learning"
|
|
7383
|
+
];
|
|
7384
|
+
for (const dir of recommendedDirs) {
|
|
7385
|
+
if (!existsSync3(join3(spacePath, dir))) {
|
|
7386
|
+
issues.push({
|
|
7387
|
+
type: "warning",
|
|
7388
|
+
path: dir,
|
|
7389
|
+
message: `Recommended directory missing: ${dir}`
|
|
7390
|
+
});
|
|
7391
|
+
}
|
|
7392
|
+
}
|
|
7393
|
+
if (!existsSync3(join3(spacePath, "CLAUDE.base.md")) && !existsSync3(join3(spacePath, "CLAUDE.md"))) {
|
|
7394
|
+
issues.push({
|
|
7395
|
+
type: "warning",
|
|
7396
|
+
path: "CLAUDE.base.md",
|
|
7397
|
+
message: "No CLAUDE context file found"
|
|
7398
|
+
});
|
|
7399
|
+
}
|
|
7400
|
+
let status = "healthy";
|
|
7401
|
+
if (issues.some((i) => i.type === "error" || i.type === "missing")) {
|
|
7402
|
+
status = "errors";
|
|
7403
|
+
} else if (issues.some((i) => i.type === "warning")) {
|
|
7404
|
+
status = "warnings";
|
|
7405
|
+
}
|
|
7406
|
+
return {
|
|
7407
|
+
space: spaceName,
|
|
7408
|
+
path: spacePath,
|
|
7409
|
+
issues,
|
|
7410
|
+
status
|
|
7411
|
+
};
|
|
7412
|
+
}
|
|
7413
|
+
var DATA_DIR2;
|
|
7414
|
+
var init_space = __esm(() => {
|
|
7415
|
+
DATA_DIR2 = join3(process.env.HOME || "~", "Data");
|
|
7416
|
+
});
|
|
7417
|
+
|
|
6983
7418
|
// src/lib/agent.ts
|
|
6984
7419
|
var exports_agent = {};
|
|
6985
7420
|
__export(exports_agent, {
|
|
@@ -6988,19 +7423,19 @@ __export(exports_agent, {
|
|
|
6988
7423
|
agentExists: () => agentExists
|
|
6989
7424
|
});
|
|
6990
7425
|
import { spawn as spawn2 } from "child_process";
|
|
6991
|
-
import { existsSync as
|
|
6992
|
-
import { join as
|
|
7426
|
+
import { existsSync as existsSync6 } from "fs";
|
|
7427
|
+
import { join as join6 } from "path";
|
|
6993
7428
|
function commandExists2(cmd) {
|
|
6994
7429
|
try {
|
|
6995
|
-
const { execSync:
|
|
6996
|
-
|
|
7430
|
+
const { execSync: execSync4 } = __require("child_process");
|
|
7431
|
+
execSync4(`which ${cmd}`, { stdio: "pipe" });
|
|
6997
7432
|
return true;
|
|
6998
7433
|
} catch {
|
|
6999
7434
|
return false;
|
|
7000
7435
|
}
|
|
7001
7436
|
}
|
|
7002
7437
|
async function invokeAgent(invocation, options = {}) {
|
|
7003
|
-
const { stream = false, cwd =
|
|
7438
|
+
const { stream = false, cwd = DATA_DIR5, timeout = 300000 } = options;
|
|
7004
7439
|
if (!commandExists2("claude")) {
|
|
7005
7440
|
return {
|
|
7006
7441
|
success: false,
|
|
@@ -7008,19 +7443,19 @@ async function invokeAgent(invocation, options = {}) {
|
|
|
7008
7443
|
error: "Claude Code CLI not found. Install with: npm install -g @anthropic-ai/claude-code"
|
|
7009
7444
|
};
|
|
7010
7445
|
}
|
|
7011
|
-
if (!
|
|
7446
|
+
if (!existsSync6(cwd)) {
|
|
7012
7447
|
return {
|
|
7013
7448
|
success: false,
|
|
7014
7449
|
output: "",
|
|
7015
7450
|
error: `Working directory not found: ${cwd}`
|
|
7016
7451
|
};
|
|
7017
7452
|
}
|
|
7018
|
-
const
|
|
7453
|
+
const prompt = buildAgentPrompt(invocation);
|
|
7019
7454
|
return new Promise((resolve) => {
|
|
7020
7455
|
const chunks = [];
|
|
7021
7456
|
let errorChunks = [];
|
|
7022
7457
|
let timedOut = false;
|
|
7023
|
-
const proc = spawn2("claude", ["--print",
|
|
7458
|
+
const proc = spawn2("claude", ["--print", prompt], {
|
|
7024
7459
|
cwd,
|
|
7025
7460
|
stdio: ["ignore", "pipe", "pipe"],
|
|
7026
7461
|
env: {
|
|
@@ -7102,15 +7537,15 @@ function parseArtifacts(output2) {
|
|
|
7102
7537
|
return artifacts;
|
|
7103
7538
|
}
|
|
7104
7539
|
function listAgents() {
|
|
7105
|
-
const registryPath =
|
|
7106
|
-
if (!
|
|
7540
|
+
const registryPath = join6(DATA_DIR5, ".datacore", "registry", "agents.yaml");
|
|
7541
|
+
if (!existsSync6(registryPath)) {
|
|
7107
7542
|
return [];
|
|
7108
7543
|
}
|
|
7109
7544
|
try {
|
|
7110
|
-
const { readFileSync:
|
|
7111
|
-
const { parse:
|
|
7112
|
-
const content =
|
|
7113
|
-
const registry =
|
|
7545
|
+
const { readFileSync: readFileSync3 } = __require("fs");
|
|
7546
|
+
const { parse: parse2 } = require_dist();
|
|
7547
|
+
const content = readFileSync3(registryPath, "utf-8");
|
|
7548
|
+
const registry = parse2(content);
|
|
7114
7549
|
return registry.agents?.map((a) => a.name) || [];
|
|
7115
7550
|
} catch {
|
|
7116
7551
|
return [];
|
|
@@ -7119,9 +7554,9 @@ function listAgents() {
|
|
|
7119
7554
|
function agentExists(name) {
|
|
7120
7555
|
return listAgents().includes(name);
|
|
7121
7556
|
}
|
|
7122
|
-
var
|
|
7557
|
+
var DATA_DIR5;
|
|
7123
7558
|
var init_agent = __esm(() => {
|
|
7124
|
-
|
|
7559
|
+
DATA_DIR5 = join6(process.env.HOME || "~", "Data");
|
|
7125
7560
|
});
|
|
7126
7561
|
|
|
7127
7562
|
// src/routing.ts
|
|
@@ -7961,6 +8396,13 @@ function getInstallCommand(pkg, platform2) {
|
|
|
7961
8396
|
windows: "winget install Python.Python.3.11",
|
|
7962
8397
|
unknown: null
|
|
7963
8398
|
},
|
|
8399
|
+
gh: {
|
|
8400
|
+
macos: "brew install gh",
|
|
8401
|
+
linux: "sudo apt-get install gh",
|
|
8402
|
+
wsl: "sudo apt-get install gh",
|
|
8403
|
+
windows: "winget install GitHub.cli",
|
|
8404
|
+
unknown: null
|
|
8405
|
+
},
|
|
7964
8406
|
claude: {
|
|
7965
8407
|
macos: "npm install -g @anthropic-ai/claude-code",
|
|
7966
8408
|
linux: "npm install -g @anthropic-ai/claude-code",
|
|
@@ -8096,6 +8538,23 @@ function checkPython(platform2) {
|
|
|
8096
8538
|
installCommand: !installed || !meetsMin ? getInstallCommand("python", platform2) ?? undefined : undefined
|
|
8097
8539
|
};
|
|
8098
8540
|
}
|
|
8541
|
+
function checkGh(platform2) {
|
|
8542
|
+
const installed = commandExists("gh");
|
|
8543
|
+
let authenticated = false;
|
|
8544
|
+
if (installed) {
|
|
8545
|
+
try {
|
|
8546
|
+
execSync2("gh auth status", { stdio: "pipe" });
|
|
8547
|
+
authenticated = true;
|
|
8548
|
+
} catch {}
|
|
8549
|
+
}
|
|
8550
|
+
return {
|
|
8551
|
+
name: "gh",
|
|
8552
|
+
required: false,
|
|
8553
|
+
installed: installed && authenticated,
|
|
8554
|
+
version: installed ? getVersion("gh") : undefined,
|
|
8555
|
+
installCommand: installed ? authenticated ? undefined : "gh auth login" : getInstallCommand("gh", platform2) ?? undefined
|
|
8556
|
+
};
|
|
8557
|
+
}
|
|
8099
8558
|
function checkClaude(platform2) {
|
|
8100
8559
|
const installed = commandExists("claude");
|
|
8101
8560
|
return {
|
|
@@ -8113,6 +8572,7 @@ function checkDependencies() {
|
|
|
8113
8572
|
checkGitLfs(platform2),
|
|
8114
8573
|
checkNode(platform2),
|
|
8115
8574
|
checkPython(platform2),
|
|
8575
|
+
checkGh(platform2),
|
|
8116
8576
|
checkClaude(platform2)
|
|
8117
8577
|
];
|
|
8118
8578
|
}
|
|
@@ -8284,291 +8744,42 @@ function getAllConfig() {
|
|
|
8284
8744
|
traceSources(value, currentPath);
|
|
8285
8745
|
} else {
|
|
8286
8746
|
let check = local;
|
|
8287
|
-
for (const p of currentPath) {
|
|
8288
|
-
if (typeof check !== "object" || check === null) {
|
|
8289
|
-
check = undefined;
|
|
8290
|
-
break;
|
|
8291
|
-
}
|
|
8292
|
-
check = check[p];
|
|
8293
|
-
}
|
|
8294
|
-
if (check !== undefined) {
|
|
8295
|
-
sources[pathStr] = "local";
|
|
8296
|
-
continue;
|
|
8297
|
-
}
|
|
8298
|
-
check = base;
|
|
8299
|
-
for (const p of currentPath) {
|
|
8300
|
-
if (typeof check !== "object" || check === null) {
|
|
8301
|
-
check = undefined;
|
|
8302
|
-
break;
|
|
8303
|
-
}
|
|
8304
|
-
check = check[p];
|
|
8305
|
-
}
|
|
8306
|
-
if (check !== undefined) {
|
|
8307
|
-
sources[pathStr] = "base";
|
|
8308
|
-
continue;
|
|
8309
|
-
}
|
|
8310
|
-
sources[pathStr] = "default";
|
|
8311
|
-
}
|
|
8312
|
-
}
|
|
8313
|
-
}
|
|
8314
|
-
traceSources(merged);
|
|
8315
|
-
return { merged, sources };
|
|
8316
|
-
}
|
|
8317
|
-
|
|
8318
|
-
// src/lib/space.ts
|
|
8319
|
-
import { existsSync as existsSync3, readdirSync, statSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
8320
|
-
import { join as join3, basename } from "path";
|
|
8321
|
-
var DATA_DIR2 = join3(process.env.HOME || "~", "Data");
|
|
8322
|
-
function listSpaces() {
|
|
8323
|
-
if (!existsSync3(DATA_DIR2)) {
|
|
8324
|
-
return [];
|
|
8325
|
-
}
|
|
8326
|
-
const entries = readdirSync(DATA_DIR2, { withFileTypes: true });
|
|
8327
|
-
const spaces = [];
|
|
8328
|
-
for (const entry of entries) {
|
|
8329
|
-
if (!entry.isDirectory())
|
|
8330
|
-
continue;
|
|
8331
|
-
const match = entry.name.match(/^(\d+)-(.+)$/);
|
|
8332
|
-
if (!match)
|
|
8333
|
-
continue;
|
|
8334
|
-
const [, numStr, name] = match;
|
|
8335
|
-
const number = parseInt(numStr, 10);
|
|
8336
|
-
const path = join3(DATA_DIR2, entry.name);
|
|
8337
|
-
spaces.push({
|
|
8338
|
-
name: entry.name,
|
|
8339
|
-
number,
|
|
8340
|
-
path,
|
|
8341
|
-
type: number === 0 ? "personal" : "team",
|
|
8342
|
-
hasGit: existsSync3(join3(path, ".git")),
|
|
8343
|
-
hasClaude: existsSync3(join3(path, "CLAUDE.md")) || existsSync3(join3(path, "CLAUDE.base.md"))
|
|
8344
|
-
});
|
|
8345
|
-
}
|
|
8346
|
-
spaces.sort((a, b) => a.number - b.number);
|
|
8347
|
-
return spaces;
|
|
8348
|
-
}
|
|
8349
|
-
function getNextSpaceNumber() {
|
|
8350
|
-
const spaces = listSpaces();
|
|
8351
|
-
if (spaces.length === 0)
|
|
8352
|
-
return 0;
|
|
8353
|
-
const maxNumber = Math.max(...spaces.map((s) => s.number));
|
|
8354
|
-
return maxNumber + 1;
|
|
8355
|
-
}
|
|
8356
|
-
function createSpace(name, type = "team") {
|
|
8357
|
-
const number = type === "personal" ? 0 : getNextSpaceNumber();
|
|
8358
|
-
const normalizedName = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
8359
|
-
const folderName = `${number}-${normalizedName}`;
|
|
8360
|
-
const spacePath = join3(DATA_DIR2, folderName);
|
|
8361
|
-
if (existsSync3(spacePath)) {
|
|
8362
|
-
throw new Error(`Space already exists: ${folderName}`);
|
|
8363
|
-
}
|
|
8364
|
-
mkdirSync2(spacePath, { recursive: true });
|
|
8365
|
-
const dirs = type === "personal" ? [
|
|
8366
|
-
".datacore",
|
|
8367
|
-
".datacore/commands",
|
|
8368
|
-
".datacore/agents",
|
|
8369
|
-
".datacore/learning",
|
|
8370
|
-
".datacore/state",
|
|
8371
|
-
".datacore/env",
|
|
8372
|
-
"org",
|
|
8373
|
-
"0-inbox",
|
|
8374
|
-
"notes",
|
|
8375
|
-
"notes/journals",
|
|
8376
|
-
"notes/pages",
|
|
8377
|
-
"notes/zettel",
|
|
8378
|
-
"journal"
|
|
8379
|
-
] : [
|
|
8380
|
-
".datacore",
|
|
8381
|
-
".datacore/commands",
|
|
8382
|
-
".datacore/agents",
|
|
8383
|
-
".datacore/learning",
|
|
8384
|
-
".datacore/state",
|
|
8385
|
-
".datacore/env",
|
|
8386
|
-
"org",
|
|
8387
|
-
"0-inbox",
|
|
8388
|
-
"journal",
|
|
8389
|
-
"1-tracks",
|
|
8390
|
-
"1-tracks/ops",
|
|
8391
|
-
"1-tracks/product",
|
|
8392
|
-
"1-tracks/dev",
|
|
8393
|
-
"1-tracks/research",
|
|
8394
|
-
"1-tracks/comms",
|
|
8395
|
-
"2-projects",
|
|
8396
|
-
"3-knowledge",
|
|
8397
|
-
"3-knowledge/pages",
|
|
8398
|
-
"3-knowledge/zettel",
|
|
8399
|
-
"3-knowledge/literature",
|
|
8400
|
-
"3-knowledge/reference",
|
|
8401
|
-
"4-archive"
|
|
8402
|
-
];
|
|
8403
|
-
for (const dir of dirs) {
|
|
8404
|
-
mkdirSync2(join3(spacePath, dir), { recursive: true });
|
|
8405
|
-
}
|
|
8406
|
-
if (type === "personal") {
|
|
8407
|
-
writeFileSync2(join3(spacePath, "org", "inbox.org"), `#+TITLE: Inbox
|
|
8408
|
-
#+FILETAGS: :inbox:
|
|
8409
|
-
|
|
8410
|
-
Capture everything here. Process daily to zero.
|
|
8411
|
-
|
|
8412
|
-
* Inbox
|
|
8413
|
-
`);
|
|
8414
|
-
writeFileSync2(join3(spacePath, "org", "next_actions.org"), `#+TITLE: Next Actions
|
|
8415
|
-
#+FILETAGS: :tasks:
|
|
8416
|
-
|
|
8417
|
-
Tasks organized by context. Tag with :AI: to delegate to agents.
|
|
8418
|
-
|
|
8419
|
-
* @computer
|
|
8420
|
-
* @phone
|
|
8421
|
-
* @errands
|
|
8422
|
-
* @home
|
|
8423
|
-
* @office
|
|
8424
|
-
* @waiting
|
|
8425
|
-
`);
|
|
8426
|
-
writeFileSync2(join3(spacePath, "org", "projects.org"), `#+TITLE: Projects
|
|
8427
|
-
#+FILETAGS: :projects:
|
|
8428
|
-
|
|
8429
|
-
Active projects (outcomes requiring multiple actions).
|
|
8430
|
-
|
|
8431
|
-
* Projects
|
|
8432
|
-
`);
|
|
8433
|
-
writeFileSync2(join3(spacePath, "org", "someday.org"), `#+TITLE: Someday/Maybe
|
|
8434
|
-
#+FILETAGS: :someday:
|
|
8435
|
-
|
|
8436
|
-
Ideas and projects for the future. Review monthly.
|
|
8437
|
-
|
|
8438
|
-
* Someday
|
|
8439
|
-
`);
|
|
8440
|
-
} else {
|
|
8441
|
-
writeFileSync2(join3(spacePath, "org", "inbox.org"), `#+TITLE: Inbox
|
|
8442
|
-
#+FILETAGS: :inbox:
|
|
8443
|
-
|
|
8444
|
-
* Capture items here
|
|
8445
|
-
`);
|
|
8446
|
-
writeFileSync2(join3(spacePath, "org", "next_actions.org"), `#+TITLE: Next Actions
|
|
8447
|
-
#+FILETAGS: :tasks:
|
|
8448
|
-
|
|
8449
|
-
* Tasks
|
|
8450
|
-
** TODO items go here
|
|
8451
|
-
`);
|
|
8452
|
-
}
|
|
8453
|
-
if (type === "personal") {
|
|
8454
|
-
writeFileSync2(join3(spacePath, "_index.md"), `# Personal Space
|
|
8455
|
-
|
|
8456
|
-
Your personal knowledge base and GTD system.
|
|
8457
|
-
|
|
8458
|
-
## GTD Workflow
|
|
8459
|
-
|
|
8460
|
-
1. **Capture** → \`org/inbox.org\` - dump everything here
|
|
8461
|
-
2. **Clarify** → Is it actionable? What's the next action?
|
|
8462
|
-
3. **Organize** → Move to \`next_actions.org\` by context
|
|
8463
|
-
4. **Reflect** → Weekly review, monthly strategic
|
|
8464
|
-
5. **Engage** → Do the work, delegate with :AI: tags
|
|
8465
|
-
|
|
8466
|
-
## Quick Links
|
|
8467
|
-
|
|
8468
|
-
- [Inbox](org/inbox.org) - Capture here
|
|
8469
|
-
- [Next Actions](org/next_actions.org) - Today's work
|
|
8470
|
-
- [Projects](org/projects.org) - Active projects
|
|
8471
|
-
- [Someday](org/someday.org) - Future ideas
|
|
8472
|
-
- [Notes](notes/) - Knowledge base
|
|
8473
|
-
- [Journal](journal/) - Daily entries
|
|
8474
|
-
`);
|
|
8475
|
-
} else {
|
|
8476
|
-
writeFileSync2(join3(spacePath, "_index.md"), `# ${name}
|
|
8477
|
-
|
|
8478
|
-
Team space.
|
|
8479
|
-
|
|
8480
|
-
## Structure
|
|
8481
|
-
|
|
8482
|
-
- \`org/\` - Task coordination
|
|
8483
|
-
- \`1-tracks/\` - Department work
|
|
8484
|
-
- \`2-projects/\` - Code repositories
|
|
8485
|
-
- \`3-knowledge/\` - Shared knowledge
|
|
8486
|
-
- \`4-archive/\` - Historical content
|
|
8487
|
-
|
|
8488
|
-
## Quick Links
|
|
8489
|
-
|
|
8490
|
-
- [Inbox](org/inbox.org)
|
|
8491
|
-
- [Tasks](org/next_actions.org)
|
|
8492
|
-
- [Journal](journal/)
|
|
8493
|
-
- [Knowledge](3-knowledge/)
|
|
8494
|
-
`);
|
|
8495
|
-
}
|
|
8496
|
-
if (type === "personal") {
|
|
8497
|
-
writeFileSync2(join3(spacePath, "CLAUDE.base.md"), `# Personal Space
|
|
8498
|
-
|
|
8499
|
-
Personal GTD system and knowledge base.
|
|
8500
|
-
|
|
8501
|
-
## GTD Workflow
|
|
8502
|
-
|
|
8503
|
-
- \`org/inbox.org\` - Single capture point, process to zero daily
|
|
8504
|
-
- \`org/next_actions.org\` - Tasks by context (@computer, @phone, etc.)
|
|
8505
|
-
- \`org/projects.org\` - Active multi-step outcomes
|
|
8506
|
-
- \`org/someday.org\` - Future ideas, review monthly
|
|
8507
|
-
|
|
8508
|
-
## AI Delegation
|
|
8509
|
-
|
|
8510
|
-
Tag tasks with \`:AI:\` to delegate to agents:
|
|
8511
|
-
- \`:AI:research:\` - Research and summarize
|
|
8512
|
-
- \`:AI:content:\` - Draft content
|
|
8513
|
-
- \`:AI:data:\` - Analyze data
|
|
8514
|
-
|
|
8515
|
-
## Knowledge
|
|
8516
|
-
|
|
8517
|
-
- \`notes/\` - Personal knowledge base (Obsidian)
|
|
8518
|
-
- \`notes/journals/\` - Daily reflections
|
|
8519
|
-
- \`notes/zettel/\` - Atomic concept notes
|
|
8520
|
-
|
|
8521
|
-
## See Also
|
|
8522
|
-
|
|
8523
|
-
Parent: ~/Data/CLAUDE.md
|
|
8524
|
-
`);
|
|
8525
|
-
} else {
|
|
8526
|
-
writeFileSync2(join3(spacePath, "CLAUDE.base.md"), `# ${name} Space
|
|
8527
|
-
|
|
8528
|
-
Team space for ${name}.
|
|
8529
|
-
|
|
8530
|
-
## Structure
|
|
8531
|
-
|
|
8532
|
-
See parent CLAUDE.md for full documentation.
|
|
8533
|
-
`);
|
|
8747
|
+
for (const p of currentPath) {
|
|
8748
|
+
if (typeof check !== "object" || check === null) {
|
|
8749
|
+
check = undefined;
|
|
8750
|
+
break;
|
|
8751
|
+
}
|
|
8752
|
+
check = check[p];
|
|
8753
|
+
}
|
|
8754
|
+
if (check !== undefined) {
|
|
8755
|
+
sources[pathStr] = "local";
|
|
8756
|
+
continue;
|
|
8757
|
+
}
|
|
8758
|
+
check = base;
|
|
8759
|
+
for (const p of currentPath) {
|
|
8760
|
+
if (typeof check !== "object" || check === null) {
|
|
8761
|
+
check = undefined;
|
|
8762
|
+
break;
|
|
8763
|
+
}
|
|
8764
|
+
check = check[p];
|
|
8765
|
+
}
|
|
8766
|
+
if (check !== undefined) {
|
|
8767
|
+
sources[pathStr] = "base";
|
|
8768
|
+
continue;
|
|
8769
|
+
}
|
|
8770
|
+
sources[pathStr] = "default";
|
|
8771
|
+
}
|
|
8772
|
+
}
|
|
8534
8773
|
}
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
type: ${type}
|
|
8538
|
-
`);
|
|
8539
|
-
writeFileSync2(join3(spacePath, ".datacore", "learning", "patterns.md"), `# Patterns
|
|
8540
|
-
|
|
8541
|
-
Successful approaches to remember.
|
|
8542
|
-
`);
|
|
8543
|
-
writeFileSync2(join3(spacePath, ".datacore", "learning", "corrections.md"), `# Corrections
|
|
8544
|
-
|
|
8545
|
-
Human feedback log.
|
|
8546
|
-
`);
|
|
8547
|
-
writeFileSync2(join3(spacePath, ".datacore", "learning", "preferences.md"), `# Preferences
|
|
8548
|
-
|
|
8549
|
-
Style and preference notes.
|
|
8550
|
-
`);
|
|
8551
|
-
writeFileSync2(join3(spacePath, ".gitignore"), type === "personal" ? `.datacore/state/
|
|
8552
|
-
.datacore/env/
|
|
8553
|
-
CLAUDE.md
|
|
8554
|
-
CLAUDE.local.md
|
|
8555
|
-
` : `.datacore/state/
|
|
8556
|
-
.datacore/env/
|
|
8557
|
-
CLAUDE.md
|
|
8558
|
-
CLAUDE.local.md
|
|
8559
|
-
2-projects/
|
|
8560
|
-
`);
|
|
8561
|
-
return {
|
|
8562
|
-
name: folderName,
|
|
8563
|
-
number,
|
|
8564
|
-
path: spacePath,
|
|
8565
|
-
type,
|
|
8566
|
-
hasGit: false,
|
|
8567
|
-
hasClaude: true
|
|
8568
|
-
};
|
|
8774
|
+
traceSources(merged);
|
|
8775
|
+
return { merged, sources };
|
|
8569
8776
|
}
|
|
8570
8777
|
|
|
8778
|
+
// src/index.ts
|
|
8779
|
+
init_space();
|
|
8780
|
+
|
|
8571
8781
|
// src/lib/sync.ts
|
|
8782
|
+
init_space();
|
|
8572
8783
|
import { execSync as execSync3 } from "child_process";
|
|
8573
8784
|
import { existsSync as existsSync4 } from "fs";
|
|
8574
8785
|
import { join as join4, basename as basename2 } from "path";
|
|
@@ -8703,46 +8914,89 @@ function getGitRepos() {
|
|
|
8703
8914
|
}
|
|
8704
8915
|
|
|
8705
8916
|
// src/lib/init.ts
|
|
8706
|
-
import { existsSync as
|
|
8707
|
-
import { join as
|
|
8917
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5, symlinkSync, copyFileSync, readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync2, cpSync } from "fs";
|
|
8918
|
+
import { join as join10, basename as basename5 } from "path";
|
|
8919
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
8708
8920
|
import { createInterface } from "readline";
|
|
8709
8921
|
|
|
8710
8922
|
// src/lib/module.ts
|
|
8711
8923
|
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync } from "fs";
|
|
8712
8924
|
import { join as join5, basename as basename3 } from "path";
|
|
8713
|
-
import {
|
|
8714
|
-
var DATA_DIR4 = join5(process.env.HOME || "
|
|
8925
|
+
import { execFileSync } from "child_process";
|
|
8926
|
+
var DATA_DIR4 = join5(process.env.HOME || "", "Data");
|
|
8715
8927
|
var MODULES_DIR = join5(DATA_DIR4, ".datacore", "modules");
|
|
8716
8928
|
var AVAILABLE_MODULES = [
|
|
8717
8929
|
{
|
|
8718
8930
|
name: "nightshift",
|
|
8719
|
-
description: "
|
|
8720
|
-
repo: "https://github.com/datacore-one/
|
|
8721
|
-
features: ["Queues :AI: tasks for
|
|
8931
|
+
description: "Autonomous AI task execution (local mode)",
|
|
8932
|
+
repo: "https://github.com/datacore-one/datacore-nightshift",
|
|
8933
|
+
features: ["Queues :AI: tasks for background processing", "Morning briefing with results", "Multi-persona evaluation"],
|
|
8934
|
+
core: true
|
|
8935
|
+
},
|
|
8936
|
+
{
|
|
8937
|
+
name: "health",
|
|
8938
|
+
description: "Health and wellness tracking - sleep, exercise, habits",
|
|
8939
|
+
repo: "https://github.com/datacore-one/datacore-health",
|
|
8940
|
+
features: ["Daily check-ins", "Habit tracking and streaks", "Health reports and correlations"],
|
|
8941
|
+
core: false
|
|
8722
8942
|
},
|
|
8723
8943
|
{
|
|
8724
8944
|
name: "crm",
|
|
8725
|
-
description: "
|
|
8726
|
-
repo: "https://github.com/datacore-one/
|
|
8727
|
-
features: ["Contact profiles
|
|
8945
|
+
description: "Network intelligence and contact management",
|
|
8946
|
+
repo: "https://github.com/datacore-one/datacore-crm",
|
|
8947
|
+
features: ["Contact profiles and interaction history", "Relationship tracking", "Industry landscape mapping"],
|
|
8948
|
+
core: false
|
|
8728
8949
|
},
|
|
8729
8950
|
{
|
|
8730
8951
|
name: "meetings",
|
|
8731
|
-
description: "Meeting
|
|
8732
|
-
repo: "https://github.com/datacore-one/
|
|
8733
|
-
features: ["Pre-meeting briefs", "Transcript processing", "Action item extraction"]
|
|
8952
|
+
description: "Meeting lifecycle automation",
|
|
8953
|
+
repo: "https://github.com/datacore-one/datacore-meetings",
|
|
8954
|
+
features: ["Pre-meeting briefs", "Transcript processing", "Action item extraction"],
|
|
8955
|
+
core: false
|
|
8734
8956
|
},
|
|
8735
8957
|
{
|
|
8736
|
-
name: "
|
|
8737
|
-
description: "
|
|
8738
|
-
repo: "https://github.com/datacore-one/
|
|
8739
|
-
features: ["
|
|
8958
|
+
name: "mail",
|
|
8959
|
+
description: "Email integration and processing",
|
|
8960
|
+
repo: "https://github.com/datacore-one/datacore-mail",
|
|
8961
|
+
features: ["Gmail adapter", "AI classification and routing", "Automated processing"],
|
|
8962
|
+
core: false
|
|
8963
|
+
},
|
|
8964
|
+
{
|
|
8965
|
+
name: "news",
|
|
8966
|
+
description: "Automated news aggregation with AI-scored relevance",
|
|
8967
|
+
repo: "https://github.com/datacore-one/datacore-news",
|
|
8968
|
+
features: ["Multi-source aggregation", "AI relevance scoring", "Tiered processing"],
|
|
8969
|
+
core: false
|
|
8970
|
+
},
|
|
8971
|
+
{
|
|
8972
|
+
name: "slides",
|
|
8973
|
+
description: "Presentation generation via Gamma.app",
|
|
8974
|
+
repo: "https://github.com/datacore-one/datacore-slides",
|
|
8975
|
+
features: ["Presentations via Gamma.app", "AI-powered backgrounds", "Template support"],
|
|
8976
|
+
core: false
|
|
8977
|
+
},
|
|
8978
|
+
{
|
|
8979
|
+
name: "trading",
|
|
8980
|
+
description: "Position management and trading workflows",
|
|
8981
|
+
repo: "https://github.com/datacore-one/datacore-trading",
|
|
8982
|
+
features: ["Position tracking", "Performance analytics", "Risk management"],
|
|
8983
|
+
core: false
|
|
8984
|
+
},
|
|
8985
|
+
{
|
|
8986
|
+
name: "telegram",
|
|
8987
|
+
description: "Mobile access to Claude Code via Telegram",
|
|
8988
|
+
repo: "https://github.com/datacore-one/datacore-telegram",
|
|
8989
|
+
features: ["Message relay", "Command execution", "File sharing"],
|
|
8990
|
+
core: false
|
|
8991
|
+
},
|
|
8992
|
+
{
|
|
8993
|
+
name: "campaigns",
|
|
8994
|
+
description: "Landing pages, deployment, and A/B testing",
|
|
8995
|
+
repo: "https://github.com/datacore-one/datacore-campaigns",
|
|
8996
|
+
features: ["Landing page generation", "PostHog analytics", "A/B testing"],
|
|
8997
|
+
core: false
|
|
8740
8998
|
}
|
|
8741
8999
|
];
|
|
8742
|
-
function getAvailableModules() {
|
|
8743
|
-
const installed = listModules().map((m) => m.name);
|
|
8744
|
-
return AVAILABLE_MODULES.filter((m) => !installed.includes(m.name));
|
|
8745
|
-
}
|
|
8746
9000
|
function listModules() {
|
|
8747
9001
|
if (!existsSync5(MODULES_DIR)) {
|
|
8748
9002
|
return [];
|
|
@@ -8788,108 +9042,362 @@ function getModuleInfo(modulePath) {
|
|
|
8788
9042
|
}
|
|
8789
9043
|
}
|
|
8790
9044
|
}
|
|
8791
|
-
const commandsDir = join5(modulePath, "commands");
|
|
8792
|
-
const commands = [];
|
|
8793
|
-
if (existsSync5(commandsDir)) {
|
|
8794
|
-
const cmdFiles = readdirSync2(commandsDir);
|
|
8795
|
-
for (const file of cmdFiles) {
|
|
8796
|
-
if (file.endsWith(".md")) {
|
|
8797
|
-
commands.push(file.replace(".md", ""));
|
|
9045
|
+
const commandsDir = join5(modulePath, "commands");
|
|
9046
|
+
const commands = [];
|
|
9047
|
+
if (existsSync5(commandsDir)) {
|
|
9048
|
+
const cmdFiles = readdirSync2(commandsDir);
|
|
9049
|
+
for (const file of cmdFiles) {
|
|
9050
|
+
if (file.endsWith(".md")) {
|
|
9051
|
+
commands.push(file.replace(".md", ""));
|
|
9052
|
+
}
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
9055
|
+
return {
|
|
9056
|
+
name,
|
|
9057
|
+
path: modulePath,
|
|
9058
|
+
version,
|
|
9059
|
+
description,
|
|
9060
|
+
agents,
|
|
9061
|
+
commands
|
|
9062
|
+
};
|
|
9063
|
+
}
|
|
9064
|
+
function installModule(source) {
|
|
9065
|
+
if (!existsSync5(MODULES_DIR)) {
|
|
9066
|
+
const { mkdirSync: mkdirSync3 } = __require("fs");
|
|
9067
|
+
mkdirSync3(MODULES_DIR, { recursive: true });
|
|
9068
|
+
}
|
|
9069
|
+
let name;
|
|
9070
|
+
let isGit = false;
|
|
9071
|
+
if (source.includes("github.com") || source.startsWith("git@") || source.endsWith(".git")) {
|
|
9072
|
+
isGit = true;
|
|
9073
|
+
const match = source.match(/([^/]+?)(?:\.git)?$/);
|
|
9074
|
+
name = match?.[1] ?? source.split("/").pop() ?? "unknown";
|
|
9075
|
+
name = name.replace(/^module-/, "").replace(/^datacore-/, "");
|
|
9076
|
+
} else if (source.startsWith("@")) {
|
|
9077
|
+
name = source.split("/").pop()?.replace(/^datacore-/, "") ?? "unknown";
|
|
9078
|
+
} else {
|
|
9079
|
+
name = source.replace(/^datacore-/, "").replace(/^module-/, "");
|
|
9080
|
+
}
|
|
9081
|
+
const modulePath = join5(MODULES_DIR, name);
|
|
9082
|
+
if (existsSync5(modulePath)) {
|
|
9083
|
+
throw new Error(`Module already installed: ${name}`);
|
|
9084
|
+
}
|
|
9085
|
+
if (isGit) {
|
|
9086
|
+
try {
|
|
9087
|
+
execFileSync("git", ["clone", source, modulePath], { stdio: "pipe", timeout: 300000 });
|
|
9088
|
+
} catch {
|
|
9089
|
+
const sshUrl = source.replace("https://github.com/", "git@github.com:");
|
|
9090
|
+
try {
|
|
9091
|
+
execFileSync("git", ["clone", sshUrl, modulePath], { stdio: "pipe", timeout: 300000 });
|
|
9092
|
+
} catch {
|
|
9093
|
+
throw new Error(`Could not clone module: ${source} (tried HTTPS and SSH)`);
|
|
9094
|
+
}
|
|
9095
|
+
}
|
|
9096
|
+
} else {
|
|
9097
|
+
const catalogEntry = AVAILABLE_MODULES.find((m) => m.name === name);
|
|
9098
|
+
if (catalogEntry) {
|
|
9099
|
+
return installModule(catalogEntry.repo);
|
|
9100
|
+
}
|
|
9101
|
+
const urls = [
|
|
9102
|
+
`https://github.com/datacore-one/datacore-${name}`,
|
|
9103
|
+
`https://github.com/datacore-one/module-${name}`
|
|
9104
|
+
];
|
|
9105
|
+
let installed = false;
|
|
9106
|
+
for (const url of urls) {
|
|
9107
|
+
try {
|
|
9108
|
+
execFileSync("git", ["clone", url, modulePath], { stdio: "pipe", timeout: 300000 });
|
|
9109
|
+
installed = true;
|
|
9110
|
+
break;
|
|
9111
|
+
} catch {
|
|
9112
|
+
continue;
|
|
9113
|
+
}
|
|
9114
|
+
}
|
|
9115
|
+
if (!installed) {
|
|
9116
|
+
throw new Error(`Could not install module "${source}". Try providing a full git URL.`);
|
|
9117
|
+
}
|
|
9118
|
+
}
|
|
9119
|
+
const info2 = getModuleInfo(modulePath);
|
|
9120
|
+
if (!info2) {
|
|
9121
|
+
rmSync(modulePath, { recursive: true, force: true });
|
|
9122
|
+
throw new Error("Invalid module: missing module.yaml");
|
|
9123
|
+
}
|
|
9124
|
+
return info2;
|
|
9125
|
+
}
|
|
9126
|
+
function updateModules(name) {
|
|
9127
|
+
const modules = listModules();
|
|
9128
|
+
const results = [];
|
|
9129
|
+
const toUpdate = name ? modules.filter((m) => m.name === name) : modules;
|
|
9130
|
+
for (const mod of toUpdate) {
|
|
9131
|
+
try {
|
|
9132
|
+
const gitDir = join5(mod.path, ".git");
|
|
9133
|
+
if (existsSync5(gitDir)) {
|
|
9134
|
+
execFileSync("git", ["pull"], { cwd: mod.path, stdio: "pipe", timeout: 60000 });
|
|
9135
|
+
results.push({ name: mod.name, updated: true });
|
|
9136
|
+
} else {
|
|
9137
|
+
results.push({ name: mod.name, updated: false, error: "Not a git repository" });
|
|
9138
|
+
}
|
|
9139
|
+
} catch (err) {
|
|
9140
|
+
results.push({ name: mod.name, updated: false, error: err.message });
|
|
9141
|
+
}
|
|
9142
|
+
}
|
|
9143
|
+
return results;
|
|
9144
|
+
}
|
|
9145
|
+
function removeModule(name) {
|
|
9146
|
+
const modulePath = join5(MODULES_DIR, name);
|
|
9147
|
+
if (!existsSync5(modulePath)) {
|
|
9148
|
+
return false;
|
|
9149
|
+
}
|
|
9150
|
+
rmSync(modulePath, { recursive: true, force: true });
|
|
9151
|
+
return true;
|
|
9152
|
+
}
|
|
9153
|
+
|
|
9154
|
+
// src/lib/init.ts
|
|
9155
|
+
init_space();
|
|
9156
|
+
init_agent();
|
|
9157
|
+
|
|
9158
|
+
// src/lib/snapshot.ts
|
|
9159
|
+
init_space();
|
|
9160
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
9161
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
9162
|
+
import { execSync as execSync4 } from "child_process";
|
|
9163
|
+
import { join as join7 } from "path";
|
|
9164
|
+
var DATA_DIR6 = join7(process.env.HOME || "~", "Data");
|
|
9165
|
+
var LOCK_FILE = join7(DATA_DIR6, "datacore.lock.yaml");
|
|
9166
|
+
var CLI_VERSION = "1.0.6";
|
|
9167
|
+
function getGitInfo(path) {
|
|
9168
|
+
if (!existsSync7(join7(path, ".git"))) {
|
|
9169
|
+
return {};
|
|
9170
|
+
}
|
|
9171
|
+
try {
|
|
9172
|
+
const remote = execSync4("git remote get-url origin 2>/dev/null || true", {
|
|
9173
|
+
cwd: path,
|
|
9174
|
+
encoding: "utf-8"
|
|
9175
|
+
}).trim() || undefined;
|
|
9176
|
+
const commit = execSync4("git rev-parse HEAD 2>/dev/null || true", {
|
|
9177
|
+
cwd: path,
|
|
9178
|
+
encoding: "utf-8"
|
|
9179
|
+
}).trim() || undefined;
|
|
9180
|
+
const branch = execSync4("git branch --show-current 2>/dev/null || true", {
|
|
9181
|
+
cwd: path,
|
|
9182
|
+
encoding: "utf-8"
|
|
9183
|
+
}).trim() || undefined;
|
|
9184
|
+
return { remote, commit, branch };
|
|
9185
|
+
} catch {
|
|
9186
|
+
return {};
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
function createSnapshot(options = {}) {
|
|
9190
|
+
const { includeSettings = false } = options;
|
|
9191
|
+
const modules = [];
|
|
9192
|
+
for (const mod of listModules()) {
|
|
9193
|
+
const gitInfo = getGitInfo(mod.path);
|
|
9194
|
+
modules.push({
|
|
9195
|
+
name: mod.name,
|
|
9196
|
+
source: gitInfo.remote || "local",
|
|
9197
|
+
version: mod.version,
|
|
9198
|
+
commit: gitInfo.commit,
|
|
9199
|
+
branch: gitInfo.branch
|
|
9200
|
+
});
|
|
9201
|
+
}
|
|
9202
|
+
const spaces = [];
|
|
9203
|
+
for (const space of listSpaces()) {
|
|
9204
|
+
const gitInfo = getGitInfo(space.path);
|
|
9205
|
+
spaces.push({
|
|
9206
|
+
name: space.name,
|
|
9207
|
+
number: space.number,
|
|
9208
|
+
type: space.type,
|
|
9209
|
+
source: gitInfo.remote,
|
|
9210
|
+
commit: gitInfo.commit
|
|
9211
|
+
});
|
|
9212
|
+
}
|
|
9213
|
+
const deps = checkDependencies();
|
|
9214
|
+
const dependencies = deps.filter((d) => d.installed && d.version).map((d) => ({
|
|
9215
|
+
name: d.name,
|
|
9216
|
+
version: d.version,
|
|
9217
|
+
required: d.required
|
|
9218
|
+
}));
|
|
9219
|
+
const snapshot = {
|
|
9220
|
+
version: "1.0",
|
|
9221
|
+
created: new Date().toISOString(),
|
|
9222
|
+
cliVersion: CLI_VERSION,
|
|
9223
|
+
platform: `${process.platform}-${process.arch}`,
|
|
9224
|
+
modules,
|
|
9225
|
+
spaces,
|
|
9226
|
+
dependencies
|
|
9227
|
+
};
|
|
9228
|
+
if (includeSettings) {
|
|
9229
|
+
const settingsPath = join7(DATA_DIR6, ".datacore", "settings.yaml");
|
|
9230
|
+
if (existsSync7(settingsPath)) {
|
|
9231
|
+
try {
|
|
9232
|
+
const content = readFileSync3(settingsPath, "utf-8");
|
|
9233
|
+
snapshot.settings = import_yaml2.parse(content);
|
|
9234
|
+
} catch {}
|
|
9235
|
+
}
|
|
9236
|
+
}
|
|
9237
|
+
return snapshot;
|
|
9238
|
+
}
|
|
9239
|
+
function saveSnapshot(snapshot, path) {
|
|
9240
|
+
const lockPath = path || LOCK_FILE;
|
|
9241
|
+
const content = import_yaml2.stringify(snapshot, {
|
|
9242
|
+
lineWidth: 0
|
|
9243
|
+
});
|
|
9244
|
+
writeFileSync3(lockPath, content);
|
|
9245
|
+
return lockPath;
|
|
9246
|
+
}
|
|
9247
|
+
function loadSnapshot(path) {
|
|
9248
|
+
const lockPath = path || LOCK_FILE;
|
|
9249
|
+
if (!existsSync7(lockPath)) {
|
|
9250
|
+
return null;
|
|
9251
|
+
}
|
|
9252
|
+
try {
|
|
9253
|
+
const content = readFileSync3(lockPath, "utf-8");
|
|
9254
|
+
return import_yaml2.parse(content);
|
|
9255
|
+
} catch {
|
|
9256
|
+
return null;
|
|
9257
|
+
}
|
|
9258
|
+
}
|
|
9259
|
+
function diffSnapshot(snapshot) {
|
|
9260
|
+
const current = createSnapshot();
|
|
9261
|
+
const diff = {
|
|
9262
|
+
modules: { added: [], removed: [], changed: [] },
|
|
9263
|
+
spaces: { added: [], removed: [] },
|
|
9264
|
+
dependencies: { changed: [] }
|
|
9265
|
+
};
|
|
9266
|
+
const currentModules = new Map(current.modules.map((m) => [m.name, m]));
|
|
9267
|
+
const snapshotModules = new Map(snapshot.modules.map((m) => [m.name, m]));
|
|
9268
|
+
for (const [name, mod] of currentModules) {
|
|
9269
|
+
if (!snapshotModules.has(name)) {
|
|
9270
|
+
diff.modules.added.push(name);
|
|
9271
|
+
} else {
|
|
9272
|
+
const expected = snapshotModules.get(name);
|
|
9273
|
+
if (expected.commit && mod.commit && expected.commit !== mod.commit) {
|
|
9274
|
+
diff.modules.changed.push({
|
|
9275
|
+
name,
|
|
9276
|
+
from: expected.commit.slice(0, 7),
|
|
9277
|
+
to: mod.commit.slice(0, 7)
|
|
9278
|
+
});
|
|
8798
9279
|
}
|
|
8799
9280
|
}
|
|
8800
9281
|
}
|
|
8801
|
-
|
|
8802
|
-
name
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
description,
|
|
8806
|
-
agents,
|
|
8807
|
-
commands
|
|
8808
|
-
};
|
|
8809
|
-
}
|
|
8810
|
-
function installModule(source) {
|
|
8811
|
-
if (!existsSync5(MODULES_DIR)) {
|
|
8812
|
-
const { mkdirSync: mkdirSync3 } = __require("fs");
|
|
8813
|
-
mkdirSync3(MODULES_DIR, { recursive: true });
|
|
8814
|
-
}
|
|
8815
|
-
let name;
|
|
8816
|
-
let isGit = false;
|
|
8817
|
-
if (source.includes("github.com") || source.startsWith("git@") || source.endsWith(".git")) {
|
|
8818
|
-
isGit = true;
|
|
8819
|
-
const match = source.match(/([^/]+?)(?:\.git)?$/);
|
|
8820
|
-
name = match?.[1] ?? source.split("/").pop() ?? "unknown";
|
|
8821
|
-
name = name.replace(/^module-/, "");
|
|
8822
|
-
} else if (source.startsWith("@")) {
|
|
8823
|
-
name = source.split("/").pop()?.replace(/^datacore-/, "") ?? "unknown";
|
|
8824
|
-
} else {
|
|
8825
|
-
name = source.replace(/^datacore-/, "").replace(/^module-/, "");
|
|
9282
|
+
for (const name of snapshotModules.keys()) {
|
|
9283
|
+
if (!currentModules.has(name)) {
|
|
9284
|
+
diff.modules.removed.push(name);
|
|
9285
|
+
}
|
|
8826
9286
|
}
|
|
8827
|
-
const
|
|
8828
|
-
|
|
8829
|
-
|
|
9287
|
+
const currentSpaces = new Set(current.spaces.map((s) => s.name));
|
|
9288
|
+
const snapshotSpaces = new Set(snapshot.spaces.map((s) => s.name));
|
|
9289
|
+
for (const name of currentSpaces) {
|
|
9290
|
+
if (!snapshotSpaces.has(name)) {
|
|
9291
|
+
diff.spaces.added.push(name);
|
|
9292
|
+
}
|
|
8830
9293
|
}
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
const gitUrl = `https://github.com/datacore/module-${name}.git`;
|
|
8835
|
-
try {
|
|
8836
|
-
execSync4(`git clone ${gitUrl} "${modulePath}"`, { stdio: "pipe" });
|
|
8837
|
-
} catch {
|
|
8838
|
-
throw new Error(`Could not install module: ${source}`);
|
|
9294
|
+
for (const name of snapshotSpaces) {
|
|
9295
|
+
if (!currentSpaces.has(name)) {
|
|
9296
|
+
diff.spaces.removed.push(name);
|
|
8839
9297
|
}
|
|
8840
9298
|
}
|
|
8841
|
-
const
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
9299
|
+
const currentDeps = new Map(current.dependencies.map((d) => [d.name, d.version]));
|
|
9300
|
+
for (const dep of snapshot.dependencies) {
|
|
9301
|
+
const actualVersion = currentDeps.get(dep.name);
|
|
9302
|
+
if (actualVersion && actualVersion !== dep.version) {
|
|
9303
|
+
diff.dependencies.changed.push({
|
|
9304
|
+
name: dep.name,
|
|
9305
|
+
expected: dep.version,
|
|
9306
|
+
actual: actualVersion
|
|
9307
|
+
});
|
|
9308
|
+
}
|
|
8845
9309
|
}
|
|
8846
|
-
return
|
|
9310
|
+
return diff;
|
|
8847
9311
|
}
|
|
8848
|
-
function
|
|
8849
|
-
const modules =
|
|
8850
|
-
const
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
9312
|
+
function restoreFromSnapshot(snapshot, options = {}) {
|
|
9313
|
+
const { modules = true, spaces = true, dryRun = false } = options;
|
|
9314
|
+
const result = {
|
|
9315
|
+
modulesInstalled: [],
|
|
9316
|
+
modulesFailed: [],
|
|
9317
|
+
spacesCreated: [],
|
|
9318
|
+
warnings: []
|
|
9319
|
+
};
|
|
9320
|
+
if (modules) {
|
|
9321
|
+
const currentModules = new Set(listModules().map((m) => m.name));
|
|
9322
|
+
for (const mod of snapshot.modules) {
|
|
9323
|
+
if (currentModules.has(mod.name)) {
|
|
9324
|
+
continue;
|
|
9325
|
+
}
|
|
9326
|
+
if (mod.source === "local") {
|
|
9327
|
+
result.warnings.push(`Module ${mod.name} was local, cannot restore`);
|
|
9328
|
+
continue;
|
|
9329
|
+
}
|
|
9330
|
+
if (dryRun) {
|
|
9331
|
+
result.modulesInstalled.push(mod.name);
|
|
9332
|
+
continue;
|
|
9333
|
+
}
|
|
9334
|
+
try {
|
|
9335
|
+
const modulesDir = join7(DATA_DIR6, ".datacore", "modules");
|
|
9336
|
+
if (!existsSync7(modulesDir)) {
|
|
9337
|
+
mkdirSync3(modulesDir, { recursive: true });
|
|
9338
|
+
}
|
|
9339
|
+
const modulePath = join7(modulesDir, mod.name);
|
|
9340
|
+
execSync4(`git clone ${mod.source} "${modulePath}"`, { stdio: "pipe" });
|
|
9341
|
+
if (mod.commit) {
|
|
9342
|
+
execSync4(`git checkout ${mod.commit}`, { cwd: modulePath, stdio: "pipe" });
|
|
9343
|
+
} else if (mod.branch) {
|
|
9344
|
+
execSync4(`git checkout ${mod.branch}`, { cwd: modulePath, stdio: "pipe" });
|
|
9345
|
+
}
|
|
9346
|
+
result.modulesInstalled.push(mod.name);
|
|
9347
|
+
} catch (err) {
|
|
9348
|
+
result.modulesFailed.push({
|
|
9349
|
+
name: mod.name,
|
|
9350
|
+
error: err.message
|
|
9351
|
+
});
|
|
8860
9352
|
}
|
|
8861
|
-
} catch (err) {
|
|
8862
|
-
results.push({ name: mod.name, updated: false, error: err.message });
|
|
8863
9353
|
}
|
|
8864
9354
|
}
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
9355
|
+
if (spaces) {
|
|
9356
|
+
const currentSpaces = new Set(listSpaces().map((s) => s.name));
|
|
9357
|
+
for (const space of snapshot.spaces) {
|
|
9358
|
+
if (currentSpaces.has(space.name)) {
|
|
9359
|
+
continue;
|
|
9360
|
+
}
|
|
9361
|
+
if (dryRun) {
|
|
9362
|
+
result.spacesCreated.push(space.name);
|
|
9363
|
+
continue;
|
|
9364
|
+
}
|
|
9365
|
+
if (space.source) {
|
|
9366
|
+
try {
|
|
9367
|
+
const spacePath = join7(DATA_DIR6, space.name);
|
|
9368
|
+
execSync4(`git clone ${space.source} "${spacePath}"`, { stdio: "pipe" });
|
|
9369
|
+
if (space.commit) {
|
|
9370
|
+
execSync4(`git checkout ${space.commit}`, { cwd: spacePath, stdio: "pipe" });
|
|
9371
|
+
}
|
|
9372
|
+
result.spacesCreated.push(space.name);
|
|
9373
|
+
} catch (err) {
|
|
9374
|
+
result.warnings.push(`Could not clone space ${space.name}: ${err.message}`);
|
|
9375
|
+
}
|
|
9376
|
+
} else {
|
|
9377
|
+
result.warnings.push(`Space ${space.name} has no git source, skipping`);
|
|
9378
|
+
}
|
|
9379
|
+
}
|
|
8871
9380
|
}
|
|
8872
|
-
|
|
8873
|
-
return true;
|
|
9381
|
+
return result;
|
|
8874
9382
|
}
|
|
8875
9383
|
|
|
8876
9384
|
// src/state.ts
|
|
8877
|
-
import { existsSync as
|
|
8878
|
-
import { join as
|
|
8879
|
-
var STATE_DIR =
|
|
8880
|
-
var STATE_FILE =
|
|
9385
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
9386
|
+
import { join as join8 } from "path";
|
|
9387
|
+
var STATE_DIR = join8(process.env.HOME || "~", "Data", ".datacore", "state");
|
|
9388
|
+
var STATE_FILE = join8(STATE_DIR, "operations.json");
|
|
8881
9389
|
function ensureStateDir() {
|
|
8882
|
-
if (!
|
|
8883
|
-
|
|
9390
|
+
if (!existsSync8(STATE_DIR)) {
|
|
9391
|
+
mkdirSync4(STATE_DIR, { recursive: true });
|
|
8884
9392
|
}
|
|
8885
9393
|
}
|
|
8886
9394
|
function loadState() {
|
|
8887
9395
|
ensureStateDir();
|
|
8888
|
-
if (!
|
|
9396
|
+
if (!existsSync8(STATE_FILE)) {
|
|
8889
9397
|
return { operations: [] };
|
|
8890
9398
|
}
|
|
8891
9399
|
try {
|
|
8892
|
-
const content =
|
|
9400
|
+
const content = readFileSync4(STATE_FILE, "utf-8");
|
|
8893
9401
|
return JSON.parse(content);
|
|
8894
9402
|
} catch {
|
|
8895
9403
|
return { operations: [] };
|
|
@@ -8897,7 +9405,7 @@ function loadState() {
|
|
|
8897
9405
|
}
|
|
8898
9406
|
function saveState(state) {
|
|
8899
9407
|
ensureStateDir();
|
|
8900
|
-
|
|
9408
|
+
writeFileSync4(STATE_FILE, JSON.stringify(state, null, 2));
|
|
8901
9409
|
}
|
|
8902
9410
|
function generateId() {
|
|
8903
9411
|
return `op_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -8914,7 +9422,8 @@ class Operation {
|
|
|
8914
9422
|
status: "pending",
|
|
8915
9423
|
startedAt: new Date().toISOString(),
|
|
8916
9424
|
steps: [],
|
|
8917
|
-
rollback: []
|
|
9425
|
+
rollback: [],
|
|
9426
|
+
meta: Object.keys(params).length > 0 ? params : undefined
|
|
8918
9427
|
};
|
|
8919
9428
|
this.store.operations.push(this.state);
|
|
8920
9429
|
saveState(this.store);
|
|
@@ -9006,16 +9515,12 @@ ${c.reset}${c.dim} AI-Powered Second Brain ${c.reset}
|
|
|
9006
9515
|
`;
|
|
9007
9516
|
var INIT_COMPLETE = `
|
|
9008
9517
|
${c.green}${c.bright}
|
|
9009
|
-
|
|
9010
|
-
║
|
|
9011
|
-
║ ${c.reset}${c.green}
|
|
9012
|
-
║ ${c.reset}${c.green}
|
|
9013
|
-
║
|
|
9014
|
-
|
|
9015
|
-
║ ${c.reset}${c.green}██║ ██║╚██████╗ ██║ ██║ ╚████╔╝ ███████╗${c.bright} ║
|
|
9016
|
-
║ ${c.reset}${c.green}╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═══╝ ╚══════╝${c.bright} ║
|
|
9017
|
-
║ ║
|
|
9018
|
-
╚═══════════════════════════════════════════════════════════════╝
|
|
9518
|
+
╔══════════════════════════════════════════════════════════╗
|
|
9519
|
+
║ ║
|
|
9520
|
+
║ ${c.reset}${c.green}██ D A T A C O R E S Y S T E M O N L I N E ██${c.bright} ║
|
|
9521
|
+
║ ${c.reset}${c.dim} ▸ All systems nominal. Ready to engage. ◂${c.green}${c.bright} ║
|
|
9522
|
+
║ ║
|
|
9523
|
+
╚══════════════════════════════════════════════════════════╝
|
|
9019
9524
|
${c.reset}
|
|
9020
9525
|
`;
|
|
9021
9526
|
function sleep(ms) {
|
|
@@ -9062,9 +9567,76 @@ function section(title) {
|
|
|
9062
9567
|
console.log(`${c.dim}${"─".repeat(50)}${c.reset}`);
|
|
9063
9568
|
}
|
|
9064
9569
|
|
|
9570
|
+
// src/lib/background.ts
|
|
9571
|
+
import { spawn as spawn3 } from "child_process";
|
|
9572
|
+
import { openSync, closeSync, mkdirSync as mkdirSync5 } from "fs";
|
|
9573
|
+
import { join as join9 } from "path";
|
|
9574
|
+
var DATA_DIR7 = join9(process.env.HOME || "", "Data");
|
|
9575
|
+
var STATE_DIR2 = join9(DATA_DIR7, ".datacore", "state");
|
|
9576
|
+
function spawnBackground(command, args, label) {
|
|
9577
|
+
mkdirSync5(STATE_DIR2, { recursive: true });
|
|
9578
|
+
const timestamp = Date.now();
|
|
9579
|
+
const logFile = join9(STATE_DIR2, `bg-${label}-${timestamp}.log`);
|
|
9580
|
+
let outFd;
|
|
9581
|
+
try {
|
|
9582
|
+
outFd = openSync(logFile, "a");
|
|
9583
|
+
const child = spawn3(command, args, {
|
|
9584
|
+
detached: true,
|
|
9585
|
+
stdio: ["ignore", outFd, outFd],
|
|
9586
|
+
cwd: DATA_DIR7,
|
|
9587
|
+
env: { ...process.env }
|
|
9588
|
+
});
|
|
9589
|
+
child.unref();
|
|
9590
|
+
try {
|
|
9591
|
+
closeSync(outFd);
|
|
9592
|
+
} catch {}
|
|
9593
|
+
return {
|
|
9594
|
+
pid: child.pid ?? -1,
|
|
9595
|
+
logFile,
|
|
9596
|
+
label,
|
|
9597
|
+
startedAt: new Date().toISOString()
|
|
9598
|
+
};
|
|
9599
|
+
} catch {
|
|
9600
|
+
if (outFd !== undefined) {
|
|
9601
|
+
try {
|
|
9602
|
+
closeSync(outFd);
|
|
9603
|
+
} catch {}
|
|
9604
|
+
}
|
|
9605
|
+
return null;
|
|
9606
|
+
}
|
|
9607
|
+
}
|
|
9608
|
+
|
|
9065
9609
|
// src/lib/init.ts
|
|
9066
|
-
var
|
|
9067
|
-
var DATACORE_DIR =
|
|
9610
|
+
var DATA_DIR8 = join10(process.env.HOME || "", "Data");
|
|
9611
|
+
var DATACORE_DIR = join10(DATA_DIR8, ".datacore");
|
|
9612
|
+
var UPSTREAM_REPO = "datacore-one/datacore";
|
|
9613
|
+
var TOTAL_STEPS = 9;
|
|
9614
|
+
var KNOWN_SPACES = [
|
|
9615
|
+
{
|
|
9616
|
+
name: "datafund",
|
|
9617
|
+
displayName: "Datafund",
|
|
9618
|
+
description: "Datafund organization - strategy, operations, investor relations",
|
|
9619
|
+
repo: "https://github.com/datacore-one/datafund-space.git",
|
|
9620
|
+
org: "datacore-one",
|
|
9621
|
+
private: true
|
|
9622
|
+
},
|
|
9623
|
+
{
|
|
9624
|
+
name: "fds",
|
|
9625
|
+
displayName: "Fair Data Society",
|
|
9626
|
+
description: "FDS projects - Fairdrive, Fairdrop, fairOS, identity",
|
|
9627
|
+
repo: "https://github.com/fairDataSociety/fds-space.git",
|
|
9628
|
+
org: "fairDataSociety",
|
|
9629
|
+
private: false
|
|
9630
|
+
},
|
|
9631
|
+
{
|
|
9632
|
+
name: "datacore",
|
|
9633
|
+
displayName: "Datacore",
|
|
9634
|
+
description: "Datacore system development - specs, CLI, modules",
|
|
9635
|
+
repo: "https://github.com/datacore-one/datacore-space.git",
|
|
9636
|
+
org: "datacore-one",
|
|
9637
|
+
private: false
|
|
9638
|
+
}
|
|
9639
|
+
];
|
|
9068
9640
|
var c2 = {
|
|
9069
9641
|
reset: "\x1B[0m",
|
|
9070
9642
|
bold: "\x1B[1m",
|
|
@@ -9088,20 +9660,220 @@ async function prompt(question, defaultValue) {
|
|
|
9088
9660
|
});
|
|
9089
9661
|
});
|
|
9090
9662
|
}
|
|
9091
|
-
async function confirm(question, defaultYes = true) {
|
|
9092
|
-
const hint = defaultYes ? "[Y/n]" : "[y/N]";
|
|
9093
|
-
const answer = await prompt(`${question} ${hint}`);
|
|
9094
|
-
if (!answer)
|
|
9095
|
-
return defaultYes;
|
|
9096
|
-
return answer.toLowerCase().startsWith("y");
|
|
9663
|
+
async function confirm(question, defaultYes = true) {
|
|
9664
|
+
const hint = defaultYes ? "[Y/n]" : "[y/N]";
|
|
9665
|
+
const answer = await prompt(`${question} ${hint}`);
|
|
9666
|
+
if (!answer)
|
|
9667
|
+
return defaultYes;
|
|
9668
|
+
return answer.toLowerCase().startsWith("y");
|
|
9669
|
+
}
|
|
9670
|
+
async function choose(question, options, defaultIndex = 0) {
|
|
9671
|
+
for (let i = 0;i < options.length; i++) {
|
|
9672
|
+
const marker = i === defaultIndex ? `${c2.cyan}>${c2.reset}` : " ";
|
|
9673
|
+
console.log(` ${marker} ${c2.cyan}${i + 1}${c2.reset}) ${options[i]}`);
|
|
9674
|
+
}
|
|
9675
|
+
console.log();
|
|
9676
|
+
const answer = await prompt(question, String(defaultIndex + 1));
|
|
9677
|
+
const num = parseInt(answer, 10);
|
|
9678
|
+
if (num >= 1 && num <= options.length)
|
|
9679
|
+
return num - 1;
|
|
9680
|
+
return defaultIndex;
|
|
9681
|
+
}
|
|
9682
|
+
function runArgs(cmd, args, opts) {
|
|
9683
|
+
try {
|
|
9684
|
+
execFileSync2(cmd, args, { stdio: "pipe", cwd: opts?.cwd, timeout: opts?.timeout });
|
|
9685
|
+
return true;
|
|
9686
|
+
} catch {
|
|
9687
|
+
return false;
|
|
9688
|
+
}
|
|
9689
|
+
}
|
|
9690
|
+
function runArgsOutput(cmd, args, opts) {
|
|
9691
|
+
try {
|
|
9692
|
+
return execFileSync2(cmd, args, { encoding: "utf-8", stdio: "pipe", timeout: opts?.timeout }).trim();
|
|
9693
|
+
} catch {
|
|
9694
|
+
return "";
|
|
9695
|
+
}
|
|
9696
|
+
}
|
|
9697
|
+
function commandExists3(cmd) {
|
|
9698
|
+
try {
|
|
9699
|
+
execFileSync2("which", [cmd], { stdio: "pipe" });
|
|
9700
|
+
return true;
|
|
9701
|
+
} catch {
|
|
9702
|
+
return false;
|
|
9703
|
+
}
|
|
9704
|
+
}
|
|
9705
|
+
function getVersionString(cmd, flag = "--version") {
|
|
9706
|
+
try {
|
|
9707
|
+
const output2 = execFileSync2(cmd, [flag], { encoding: "utf-8", stdio: "pipe" });
|
|
9708
|
+
return output2.match(/(\d+\.\d+(\.\d+)?)/)?.[0] ?? output2.trim().split(`
|
|
9709
|
+
`)[0]?.slice(0, 30);
|
|
9710
|
+
} catch {
|
|
9711
|
+
return;
|
|
9712
|
+
}
|
|
9713
|
+
}
|
|
9714
|
+
function checkGhAuth() {
|
|
9715
|
+
if (!commandExists3("gh"))
|
|
9716
|
+
return { available: false };
|
|
9717
|
+
try {
|
|
9718
|
+
const output2 = execFileSync2("gh", ["auth", "status"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 15000 });
|
|
9719
|
+
const combined = output2;
|
|
9720
|
+
const userMatch = combined.match(/Logged in to github\.com.*account (\S+)/i);
|
|
9721
|
+
return { available: true, user: userMatch?.[1] };
|
|
9722
|
+
} catch (err) {
|
|
9723
|
+
const stderr = err?.stderr?.toString() || "";
|
|
9724
|
+
const userMatch = stderr.match(/Logged in to github\.com.*account (\S+)/i);
|
|
9725
|
+
if (userMatch)
|
|
9726
|
+
return { available: true, user: userMatch[1] };
|
|
9727
|
+
return { available: false };
|
|
9728
|
+
}
|
|
9729
|
+
}
|
|
9730
|
+
function isGitConfigured() {
|
|
9731
|
+
const name = runArgsOutput("git", ["config", "--global", "user.name"]);
|
|
9732
|
+
const email = runArgsOutput("git", ["config", "--global", "user.email"]);
|
|
9733
|
+
return { name: name || undefined, email: email || undefined, configured: !!(name && email) };
|
|
9734
|
+
}
|
|
9735
|
+
async function ensureDependency(name, checkCmd, platform2, isTTY, versionFlag = "--version") {
|
|
9736
|
+
if (commandExists3(checkCmd)) {
|
|
9737
|
+
const version = getVersionString(checkCmd, versionFlag);
|
|
9738
|
+
return { available: true, version, wasInstalled: false };
|
|
9739
|
+
}
|
|
9740
|
+
const installCmd = getInstallCommand(name, platform2);
|
|
9741
|
+
if (!installCmd) {
|
|
9742
|
+
return { available: false, wasInstalled: false };
|
|
9743
|
+
}
|
|
9744
|
+
const spinner = isTTY ? new Spinner(`Installing ${name}...`) : null;
|
|
9745
|
+
spinner?.start();
|
|
9746
|
+
try {
|
|
9747
|
+
execFileSync2("/bin/bash", ["-c", installCmd], { stdio: "pipe", timeout: 300000 });
|
|
9748
|
+
if (commandExists3(checkCmd)) {
|
|
9749
|
+
const version = getVersionString(checkCmd, versionFlag);
|
|
9750
|
+
spinner?.succeed(`${name} installed${version ? ` (${version})` : ""}`);
|
|
9751
|
+
return { available: true, version, wasInstalled: true };
|
|
9752
|
+
}
|
|
9753
|
+
spinner?.fail(`${name} install completed but command not found in PATH`);
|
|
9754
|
+
if (isTTY)
|
|
9755
|
+
console.log(` ${c2.dim}Try manually: ${installCmd}${c2.reset}`);
|
|
9756
|
+
return { available: false, wasInstalled: false };
|
|
9757
|
+
} catch {
|
|
9758
|
+
spinner?.fail(`Failed to install ${name}`);
|
|
9759
|
+
if (isTTY)
|
|
9760
|
+
console.log(` ${c2.dim}Try manually: ${installCmd}${c2.reset}`);
|
|
9761
|
+
return { available: false, wasInstalled: false };
|
|
9762
|
+
}
|
|
9763
|
+
}
|
|
9764
|
+
async function ensureHomebrew(isTTY) {
|
|
9765
|
+
if (commandExists3("brew"))
|
|
9766
|
+
return true;
|
|
9767
|
+
const brewPaths = ["/opt/homebrew/bin/brew", "/usr/local/bin/brew"];
|
|
9768
|
+
for (const p of brewPaths) {
|
|
9769
|
+
if (existsSync9(p)) {
|
|
9770
|
+
const dir = join10(p, "..");
|
|
9771
|
+
process.env.PATH = `${dir}:${process.env.PATH}`;
|
|
9772
|
+
return true;
|
|
9773
|
+
}
|
|
9774
|
+
}
|
|
9775
|
+
const spinner = isTTY ? new Spinner("Installing Homebrew...") : null;
|
|
9776
|
+
spinner?.start();
|
|
9777
|
+
try {
|
|
9778
|
+
execFileSync2("/bin/bash", ["-c", 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'], {
|
|
9779
|
+
stdio: "pipe",
|
|
9780
|
+
timeout: 600000
|
|
9781
|
+
});
|
|
9782
|
+
for (const p of brewPaths) {
|
|
9783
|
+
if (existsSync9(p)) {
|
|
9784
|
+
const dir = join10(p, "..");
|
|
9785
|
+
process.env.PATH = `${dir}:${process.env.PATH}`;
|
|
9786
|
+
break;
|
|
9787
|
+
}
|
|
9788
|
+
}
|
|
9789
|
+
if (commandExists3("brew")) {
|
|
9790
|
+
spinner?.succeed("Homebrew installed");
|
|
9791
|
+
return true;
|
|
9792
|
+
}
|
|
9793
|
+
spinner?.fail("Homebrew installed but not found in PATH");
|
|
9794
|
+
return false;
|
|
9795
|
+
} catch {
|
|
9796
|
+
spinner?.fail("Failed to install Homebrew");
|
|
9797
|
+
if (isTTY) {
|
|
9798
|
+
console.log(` ${c2.dim}Install manually: https://brew.sh${c2.reset}`);
|
|
9799
|
+
}
|
|
9800
|
+
return false;
|
|
9801
|
+
}
|
|
9802
|
+
}
|
|
9803
|
+
function runModulePostInstall(modulePath) {
|
|
9804
|
+
const reqTxt = join10(modulePath, "requirements.txt");
|
|
9805
|
+
if (existsSync9(reqTxt)) {
|
|
9806
|
+
const ok = runArgs("python3", ["-m", "pip", "install", "-r", reqTxt, "--quiet"], { cwd: modulePath, timeout: 120000 });
|
|
9807
|
+
return { ran: true, success: ok, type: "pip" };
|
|
9808
|
+
}
|
|
9809
|
+
const pkgJson = join10(modulePath, "package.json");
|
|
9810
|
+
if (existsSync9(pkgJson)) {
|
|
9811
|
+
try {
|
|
9812
|
+
const pkg = JSON.parse(readFileSync5(pkgJson, "utf-8"));
|
|
9813
|
+
if (pkg.dependencies || pkg.devDependencies) {
|
|
9814
|
+
const ok = runArgs("npm", ["install", "--silent"], { cwd: modulePath, timeout: 120000 });
|
|
9815
|
+
return { ran: true, success: ok, type: "npm" };
|
|
9816
|
+
}
|
|
9817
|
+
} catch {}
|
|
9818
|
+
}
|
|
9819
|
+
const installSh = join10(modulePath, "install.sh");
|
|
9820
|
+
if (existsSync9(installSh)) {
|
|
9821
|
+
const ok = runArgs("bash", [installSh], { cwd: modulePath });
|
|
9822
|
+
return { ran: true, success: ok, type: "script" };
|
|
9823
|
+
}
|
|
9824
|
+
try {
|
|
9825
|
+
const entries = readdirSync3(modulePath, { withFileTypes: true });
|
|
9826
|
+
for (const entry of entries) {
|
|
9827
|
+
if (!entry.isDirectory() || entry.name.startsWith("."))
|
|
9828
|
+
continue;
|
|
9829
|
+
const subPkg = join10(modulePath, entry.name, "package.json");
|
|
9830
|
+
if (existsSync9(subPkg)) {
|
|
9831
|
+
try {
|
|
9832
|
+
const pkg = JSON.parse(readFileSync5(subPkg, "utf-8"));
|
|
9833
|
+
if (pkg.dependencies || pkg.devDependencies) {
|
|
9834
|
+
const ok = runArgs("npm", ["install", "--silent"], { cwd: join10(modulePath, entry.name), timeout: 120000 });
|
|
9835
|
+
return { ran: true, success: ok, type: "npm" };
|
|
9836
|
+
}
|
|
9837
|
+
} catch {}
|
|
9838
|
+
}
|
|
9839
|
+
}
|
|
9840
|
+
} catch {}
|
|
9841
|
+
return { ran: false, success: true };
|
|
9842
|
+
}
|
|
9843
|
+
function countFiles(dir) {
|
|
9844
|
+
let total = 0;
|
|
9845
|
+
const byExt = {};
|
|
9846
|
+
function walk(d) {
|
|
9847
|
+
try {
|
|
9848
|
+
for (const entry of readdirSync3(d, { withFileTypes: true })) {
|
|
9849
|
+
if (entry.name.startsWith("."))
|
|
9850
|
+
continue;
|
|
9851
|
+
const fullPath = join10(d, entry.name);
|
|
9852
|
+
if (entry.isFile()) {
|
|
9853
|
+
total++;
|
|
9854
|
+
const ext = entry.name.includes(".") ? entry.name.split(".").pop().toLowerCase() : "other";
|
|
9855
|
+
byExt[ext] = (byExt[ext] || 0) + 1;
|
|
9856
|
+
} else if (entry.isDirectory()) {
|
|
9857
|
+
walk(fullPath);
|
|
9858
|
+
}
|
|
9859
|
+
}
|
|
9860
|
+
} catch {}
|
|
9861
|
+
}
|
|
9862
|
+
walk(dir);
|
|
9863
|
+
return { total, byExt };
|
|
9097
9864
|
}
|
|
9098
9865
|
function isInitialized() {
|
|
9099
|
-
|
|
9866
|
+
try {
|
|
9867
|
+
return existsSync9(DATACORE_DIR) && existsSync9(join10(DATACORE_DIR, "agents")) && readdirSync3(join10(DATACORE_DIR, "agents")).length > 0 && existsSync9(join10(DATA_DIR8, "0-personal")) && existsSync9(join10(DATA_DIR8, ".git"));
|
|
9868
|
+
} catch {
|
|
9869
|
+
return false;
|
|
9870
|
+
}
|
|
9100
9871
|
}
|
|
9101
9872
|
async function initDatacore(options = {}) {
|
|
9102
|
-
const { nonInteractive = false, skipChecks = false, stream = false } = options;
|
|
9873
|
+
const { nonInteractive = false, skipChecks = false, stream = false, verbose = false } = options;
|
|
9103
9874
|
const isTTY = stream && process.stdout.isTTY;
|
|
9104
9875
|
const interactive = isTTY && !nonInteractive;
|
|
9876
|
+
const platform2 = detectPlatform();
|
|
9105
9877
|
const result = {
|
|
9106
9878
|
success: false,
|
|
9107
9879
|
created: [],
|
|
@@ -9113,6 +9885,7 @@ async function initDatacore(options = {}) {
|
|
|
9113
9885
|
};
|
|
9114
9886
|
const op = startOperation("init", { options });
|
|
9115
9887
|
op.start();
|
|
9888
|
+
let profile = { name: "", email: "", useCase: "both", role: "" };
|
|
9116
9889
|
try {
|
|
9117
9890
|
if (isTTY) {
|
|
9118
9891
|
console.clear();
|
|
@@ -9121,383 +9894,1024 @@ async function initDatacore(options = {}) {
|
|
|
9121
9894
|
console.log();
|
|
9122
9895
|
await sleep(300);
|
|
9123
9896
|
}
|
|
9124
|
-
op.addStep("
|
|
9125
|
-
op.startStep("
|
|
9897
|
+
op.addStep("about_you");
|
|
9898
|
+
op.startStep("about_you");
|
|
9899
|
+
if (interactive) {
|
|
9900
|
+
section(`Step 1/${TOTAL_STEPS}: About You`);
|
|
9901
|
+
console.log();
|
|
9902
|
+
console.log(` ${c2.dim}Let's personalize your setup. This configures your identity,${c2.reset}`);
|
|
9903
|
+
console.log(` ${c2.dim}AI context layer, and tailors the experience to your needs.${c2.reset}`);
|
|
9904
|
+
console.log();
|
|
9905
|
+
const gitConfig = isGitConfigured();
|
|
9906
|
+
if (gitConfig.name)
|
|
9907
|
+
profile.name = gitConfig.name;
|
|
9908
|
+
if (gitConfig.email)
|
|
9909
|
+
profile.email = gitConfig.email;
|
|
9910
|
+
profile.name = await prompt(` Your name`, profile.name || undefined);
|
|
9911
|
+
profile.email = await prompt(` Your email`, profile.email || undefined);
|
|
9912
|
+
console.log();
|
|
9913
|
+
console.log(` ${c2.bold}How will you use Datacore?${c2.reset}`);
|
|
9914
|
+
const useCaseIdx = await choose(" Choose", [
|
|
9915
|
+
"Personal productivity (GTD, knowledge management, AI delegation)",
|
|
9916
|
+
"Team management (projects, collaboration, shared knowledge)",
|
|
9917
|
+
"Both personal and team use"
|
|
9918
|
+
], 2);
|
|
9919
|
+
profile.useCase = ["personal", "team", "both"][useCaseIdx] ?? "both";
|
|
9920
|
+
console.log();
|
|
9921
|
+
profile.role = await prompt(` Your role (e.g., developer, founder, researcher)`, "");
|
|
9922
|
+
console.log();
|
|
9923
|
+
console.log(` ${c2.green}✓${c2.reset} Welcome, ${c2.bold}${profile.name || "friend"}${c2.reset}!`);
|
|
9924
|
+
console.log();
|
|
9925
|
+
} else {
|
|
9926
|
+
const gitConfig = isGitConfigured();
|
|
9927
|
+
if (gitConfig.name)
|
|
9928
|
+
profile.name = gitConfig.name;
|
|
9929
|
+
if (gitConfig.email)
|
|
9930
|
+
profile.email = gitConfig.email;
|
|
9931
|
+
}
|
|
9932
|
+
op.completeStep("about_you");
|
|
9933
|
+
op.addStep("system_setup");
|
|
9934
|
+
op.startStep("system_setup");
|
|
9126
9935
|
if (!skipChecks) {
|
|
9127
9936
|
if (isTTY) {
|
|
9128
|
-
section(
|
|
9937
|
+
section(`Step 2/${TOTAL_STEPS}: System Setup`);
|
|
9938
|
+
console.log();
|
|
9939
|
+
console.log(` ${c2.dim}Ensuring all tools are installed and configured.${c2.reset}`);
|
|
9940
|
+
console.log(` ${c2.dim}Datacore will install anything that's missing.${c2.reset}`);
|
|
9129
9941
|
console.log();
|
|
9130
9942
|
}
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
if (gitStatus.configured) {
|
|
9137
|
-
console.log(` ${c2.dim}Configured as: ${gitStatus.userName} <${gitStatus.userEmail}>${c2.reset}`);
|
|
9138
|
-
} else {
|
|
9139
|
-
console.log(` ${c2.yellow}⚠${c2.reset} ${c2.yellow}Not configured. Run:${c2.reset}`);
|
|
9140
|
-
console.log(` ${c2.dim}git config --global user.name "Your Name"${c2.reset}`);
|
|
9141
|
-
console.log(` ${c2.dim}git config --global user.email "you@example.com"${c2.reset}`);
|
|
9142
|
-
result.warnings.push("git not configured with user.name and user.email");
|
|
9943
|
+
if (platform2 === "macos") {
|
|
9944
|
+
if (!commandExists3("brew")) {
|
|
9945
|
+
if (isTTY) {
|
|
9946
|
+
console.log(` ${c2.dim}Homebrew is needed to install system packages on macOS.${c2.reset}`);
|
|
9947
|
+
console.log();
|
|
9143
9948
|
}
|
|
9144
|
-
|
|
9145
|
-
console.log(` ${c2.red}✗${c2.reset} git ${c2.red}(required)${c2.reset}`);
|
|
9146
|
-
console.log(` ${c2.dim}Version control for your knowledge system${c2.reset}`);
|
|
9147
|
-
result.errors.push("git is required but not installed");
|
|
9949
|
+
await ensureHomebrew(!!isTTY);
|
|
9148
9950
|
}
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
|
|
9153
|
-
console.log(`
|
|
9951
|
+
}
|
|
9952
|
+
const git = await ensureDependency("git", "git", platform2, !!isTTY);
|
|
9953
|
+
if (git.available) {
|
|
9954
|
+
if (!git.wasInstalled && isTTY) {
|
|
9955
|
+
console.log(` ${c2.green}✓${c2.reset} git ${c2.dim}(${git.version})${c2.reset}`);
|
|
9154
9956
|
}
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
};
|
|
9164
|
-
if (dep.installed) {
|
|
9165
|
-
console.log(` ${c2.green}✓${c2.reset} ${dep.name} ${dep.version ? `${c2.dim}(${dep.version})${c2.reset}` : ""}`);
|
|
9166
|
-
} else if (dep.required) {
|
|
9167
|
-
console.log(` ${c2.red}✗${c2.reset} ${dep.name} ${c2.red}(required)${c2.reset}`);
|
|
9168
|
-
console.log(` ${c2.dim}${info2[dep.name] || ""}${c2.reset}`);
|
|
9169
|
-
if (dep.installCommand) {
|
|
9170
|
-
console.log(` ${c2.yellow}Install: ${dep.installCommand}${c2.reset}`);
|
|
9171
|
-
}
|
|
9957
|
+
const gitConfig = isGitConfigured();
|
|
9958
|
+
if (!gitConfig.configured && profile.name && profile.email) {
|
|
9959
|
+
if (isTTY) {
|
|
9960
|
+
const spinner = new Spinner("Configuring git identity...");
|
|
9961
|
+
spinner.start();
|
|
9962
|
+
runArgs("git", ["config", "--global", "user.name", profile.name]);
|
|
9963
|
+
runArgs("git", ["config", "--global", "user.email", profile.email]);
|
|
9964
|
+
spinner.succeed(`Git configured as: ${profile.name} <${profile.email}>`);
|
|
9172
9965
|
} else {
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9966
|
+
runArgs("git", ["config", "--global", "user.name", profile.name]);
|
|
9967
|
+
runArgs("git", ["config", "--global", "user.email", profile.email]);
|
|
9968
|
+
}
|
|
9969
|
+
} else if (gitConfig.configured && interactive && profile.name && profile.email && (gitConfig.name !== profile.name || gitConfig.email !== profile.email)) {
|
|
9970
|
+
console.log(` ${c2.dim}Git configured as: ${gitConfig.name} <${gitConfig.email}>${c2.reset}`);
|
|
9971
|
+
const override = await confirm(` Update to ${profile.name} <${profile.email}>?`, false);
|
|
9972
|
+
if (override) {
|
|
9973
|
+
runArgs("git", ["config", "--global", "user.name", profile.name]);
|
|
9974
|
+
runArgs("git", ["config", "--global", "user.email", profile.email]);
|
|
9975
|
+
console.log(` ${c2.green}✓${c2.reset} Git updated to: ${profile.name} <${profile.email}>`);
|
|
9178
9976
|
}
|
|
9977
|
+
} else if (gitConfig.configured && isTTY && !git.wasInstalled) {
|
|
9978
|
+
console.log(` ${c2.dim}Configured as: ${gitConfig.name} <${gitConfig.email}>${c2.reset}`);
|
|
9179
9979
|
}
|
|
9180
|
-
|
|
9980
|
+
} else {
|
|
9981
|
+
result.errors.push("git is required but could not be installed");
|
|
9982
|
+
}
|
|
9983
|
+
const node = await ensureDependency("node", "node", platform2, !!isTTY, "-v");
|
|
9984
|
+
if (node.available && !node.wasInstalled && isTTY) {
|
|
9985
|
+
console.log(` ${c2.green}✓${c2.reset} node ${c2.dim}(${node.version})${c2.reset}`);
|
|
9986
|
+
}
|
|
9987
|
+
if (node.available && node.version) {
|
|
9988
|
+
const major = parseInt(node.version.split(".")[0] || "0", 10);
|
|
9989
|
+
if (major < 20) {
|
|
9990
|
+
if (isTTY)
|
|
9991
|
+
console.log(` ${c2.yellow}⚠${c2.reset} ${c2.dim}Node ${node.version} is outdated. Consider updating to Node 20+${c2.reset}`);
|
|
9992
|
+
result.warnings.push(`Node.js ${node.version} is outdated - recommend Node 20+`);
|
|
9993
|
+
}
|
|
9994
|
+
}
|
|
9995
|
+
const claude = await ensureDependency("claude", "claude", platform2, !!isTTY);
|
|
9996
|
+
if (claude.available && !claude.wasInstalled && isTTY) {
|
|
9997
|
+
console.log(` ${c2.green}✓${c2.reset} Claude Code ${c2.dim}(${claude.version})${c2.reset}`);
|
|
9181
9998
|
}
|
|
9182
|
-
if (
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9999
|
+
if (!claude.available) {
|
|
10000
|
+
result.warnings.push("Claude Code not installed - install with: npm install -g @anthropic-ai/claude-code");
|
|
10001
|
+
}
|
|
10002
|
+
const python = await ensureDependency("python", "python3", platform2, !!isTTY);
|
|
10003
|
+
if (python.available && !python.wasInstalled && isTTY) {
|
|
10004
|
+
console.log(` ${c2.green}✓${c2.reset} python ${c2.dim}(${python.version})${c2.reset}`);
|
|
10005
|
+
}
|
|
10006
|
+
if (!python.available) {
|
|
10007
|
+
result.warnings.push("Python not installed - some agents may not work");
|
|
10008
|
+
}
|
|
10009
|
+
const gh = await ensureDependency("gh", "gh", platform2, !!isTTY);
|
|
10010
|
+
if (gh.available && !gh.wasInstalled && isTTY) {
|
|
10011
|
+
const ghAuth2 = checkGhAuth();
|
|
10012
|
+
if (ghAuth2.available) {
|
|
10013
|
+
console.log(` ${c2.green}✓${c2.reset} GitHub CLI ${c2.dim}(authenticated as ${ghAuth2.user})${c2.reset}`);
|
|
10014
|
+
} else {
|
|
10015
|
+
console.log(` ${c2.green}✓${c2.reset} GitHub CLI ${c2.dim}(installed, not authenticated)${c2.reset}`);
|
|
9186
10016
|
}
|
|
9187
|
-
|
|
9188
|
-
|
|
10017
|
+
}
|
|
10018
|
+
if (commandExists3("gh")) {
|
|
10019
|
+
const ghAuth2 = checkGhAuth();
|
|
10020
|
+
if (!ghAuth2.available && interactive) {
|
|
9189
10021
|
console.log();
|
|
10022
|
+
console.log(` ${c2.dim}To connect your GitHub account, a browser window will open.${c2.reset}`);
|
|
10023
|
+
const doAuth = await confirm(" Authenticate with GitHub now?", true);
|
|
10024
|
+
if (doAuth) {
|
|
10025
|
+
try {
|
|
10026
|
+
execFileSync2("gh", ["auth", "login", "--web", "--git-protocol", "https"], {
|
|
10027
|
+
stdio: "inherit",
|
|
10028
|
+
timeout: 120000
|
|
10029
|
+
});
|
|
10030
|
+
const newAuth = checkGhAuth();
|
|
10031
|
+
if (newAuth.available) {
|
|
10032
|
+
console.log(` ${c2.green}✓${c2.reset} GitHub authenticated (${newAuth.user})`);
|
|
10033
|
+
}
|
|
10034
|
+
} catch {
|
|
10035
|
+
console.log(` ${c2.yellow}⚠${c2.reset} GitHub authentication skipped`);
|
|
10036
|
+
result.warnings.push("GitHub CLI not authenticated - fork workflow may not work");
|
|
10037
|
+
}
|
|
10038
|
+
} else {
|
|
10039
|
+
result.warnings.push("GitHub CLI not authenticated - run: gh auth login");
|
|
10040
|
+
}
|
|
9190
10041
|
}
|
|
9191
|
-
op.failStep("check_dependencies", "Missing required dependencies");
|
|
9192
|
-
op.fail("Missing required dependencies");
|
|
9193
|
-
return result;
|
|
9194
10042
|
}
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
10043
|
+
const gitlfs = await ensureDependency("git-lfs", "git-lfs", platform2, !!isTTY);
|
|
10044
|
+
if (gitlfs.available && !gitlfs.wasInstalled && isTTY) {
|
|
10045
|
+
console.log(` ${c2.green}✓${c2.reset} git-lfs ${c2.dim}(${gitlfs.version})${c2.reset}`);
|
|
10046
|
+
}
|
|
10047
|
+
if (isTTY) {
|
|
10048
|
+
console.log();
|
|
10049
|
+
if (git.available && node.available) {
|
|
10050
|
+
console.log(` ${c2.green}All systems ready.${c2.reset}`);
|
|
10051
|
+
} else {
|
|
10052
|
+
console.log(` ${c2.yellow}Some tools could not be installed. Continuing with what's available.${c2.reset}`);
|
|
9201
10053
|
}
|
|
10054
|
+
console.log();
|
|
9202
10055
|
}
|
|
9203
10056
|
}
|
|
9204
|
-
op.completeStep("
|
|
9205
|
-
op.addStep("
|
|
9206
|
-
op.startStep("
|
|
10057
|
+
op.completeStep("system_setup");
|
|
10058
|
+
op.addStep("clone_repo");
|
|
10059
|
+
op.startStep("clone_repo");
|
|
9207
10060
|
if (isTTY) {
|
|
9208
|
-
section(
|
|
9209
|
-
console.log(
|
|
10061
|
+
section(`Step 3/${TOTAL_STEPS}: Setting Up Repository`);
|
|
10062
|
+
console.log();
|
|
10063
|
+
console.log(` ${c2.dim}Datacore lives in ~/Data. We'll fork the main repository to your${c2.reset}`);
|
|
10064
|
+
console.log(` ${c2.dim}GitHub account so you can customize freely and pull updates.${c2.reset}`);
|
|
9210
10065
|
console.log();
|
|
9211
10066
|
}
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
result.created.push(DATA_DIR5);
|
|
10067
|
+
const ghAuth = checkGhAuth();
|
|
10068
|
+
if (existsSync9(join10(DATA_DIR8, ".git"))) {
|
|
9215
10069
|
if (isTTY)
|
|
9216
|
-
console.log(` ${c2.green}✓${c2.reset}
|
|
10070
|
+
console.log(` ${c2.green}✓${c2.reset} Found existing repository at ~/Data`);
|
|
10071
|
+
const spinner = isTTY ? new Spinner("Pulling latest changes...") : null;
|
|
10072
|
+
spinner?.start();
|
|
10073
|
+
if (runArgs("git", ["pull", "--rebase", "--autostash"], { cwd: DATA_DIR8, timeout: 60000 })) {
|
|
10074
|
+
spinner?.succeed("Repository up to date");
|
|
10075
|
+
} else {
|
|
10076
|
+
spinner?.fail("Pull failed (non-fatal, continuing)");
|
|
10077
|
+
result.warnings.push("Could not pull latest changes");
|
|
10078
|
+
}
|
|
10079
|
+
} else if (!existsSync9(DATA_DIR8) || readdirSync3(DATA_DIR8).filter((f) => !f.startsWith(".")).length === 0) {
|
|
10080
|
+
if (ghAuth.available) {
|
|
10081
|
+
const spinner = isTTY ? new Spinner("Forking repository...") : null;
|
|
10082
|
+
spinner?.start();
|
|
10083
|
+
const ghUser = ghAuth.user || runArgsOutput("gh", ["api", "user", "-q", ".login"], { timeout: 15000 });
|
|
10084
|
+
const forkExists = runArgs("gh", ["repo", "view", `${ghUser}/datacore`], { cwd: process.env.HOME, timeout: 30000 });
|
|
10085
|
+
if (!forkExists) {
|
|
10086
|
+
const forked = runArgs("gh", ["repo", "fork", UPSTREAM_REPO, "--clone=false"], { timeout: 30000 });
|
|
10087
|
+
if (forked) {
|
|
10088
|
+
spinner?.succeed(`Forked to ${ghUser}/datacore`);
|
|
10089
|
+
} else {
|
|
10090
|
+
spinner?.fail("Fork failed");
|
|
10091
|
+
result.warnings.push("Could not fork repository - will clone directly");
|
|
10092
|
+
}
|
|
10093
|
+
} else {
|
|
10094
|
+
spinner?.succeed(`Fork exists: ${ghUser}/datacore`);
|
|
10095
|
+
}
|
|
10096
|
+
const cloneSpinner = isTTY ? new Spinner("Cloning into ~/Data...") : null;
|
|
10097
|
+
cloneSpinner?.start();
|
|
10098
|
+
mkdirSync6(DATA_DIR8, { recursive: true });
|
|
10099
|
+
const cloneUrl = `https://github.com/${ghUser}/datacore.git`;
|
|
10100
|
+
let cloned = runArgs("git", ["clone", cloneUrl, "."], { cwd: DATA_DIR8, timeout: 300000 });
|
|
10101
|
+
if (!cloned) {
|
|
10102
|
+
cloned = runArgs("git", ["clone", `git@github.com:${ghUser}/datacore.git`, "."], { cwd: DATA_DIR8, timeout: 300000 });
|
|
10103
|
+
}
|
|
10104
|
+
if (!cloned) {
|
|
10105
|
+
cloned = runArgs("git", ["clone", `https://github.com/${UPSTREAM_REPO}.git`, "."], { cwd: DATA_DIR8, timeout: 300000 });
|
|
10106
|
+
if (!cloned) {
|
|
10107
|
+
cloned = runArgs("git", ["clone", `git@github.com:${UPSTREAM_REPO}.git`, "."], { cwd: DATA_DIR8, timeout: 300000 });
|
|
10108
|
+
}
|
|
10109
|
+
}
|
|
10110
|
+
if (cloned) {
|
|
10111
|
+
cloneSpinner?.succeed("Cloned into ~/Data");
|
|
10112
|
+
result.created.push(DATA_DIR8);
|
|
10113
|
+
runArgs("git", ["remote", "add", "upstream", `https://github.com/${UPSTREAM_REPO}.git`], { cwd: DATA_DIR8 });
|
|
10114
|
+
} else {
|
|
10115
|
+
cloneSpinner?.fail("Clone failed");
|
|
10116
|
+
result.errors.push("Could not clone repository");
|
|
10117
|
+
op.failStep("clone_repo", "Clone failed");
|
|
10118
|
+
op.fail("Clone failed");
|
|
10119
|
+
return result;
|
|
10120
|
+
}
|
|
10121
|
+
} else {
|
|
10122
|
+
let repoUrl;
|
|
10123
|
+
if (interactive) {
|
|
10124
|
+
console.log(` ${c2.dim}GitHub CLI not authenticated. Cloning upstream directly.${c2.reset}`);
|
|
10125
|
+
console.log(` ${c2.dim}You can fork later with: gh repo fork --remote${c2.reset}`);
|
|
10126
|
+
console.log();
|
|
10127
|
+
repoUrl = await prompt(" Repository URL", `https://github.com/${UPSTREAM_REPO}.git`);
|
|
10128
|
+
} else {
|
|
10129
|
+
repoUrl = `https://github.com/${UPSTREAM_REPO}.git`;
|
|
10130
|
+
}
|
|
10131
|
+
mkdirSync6(DATA_DIR8, { recursive: true });
|
|
10132
|
+
const spinner = isTTY ? new Spinner("Cloning into ~/Data...") : null;
|
|
10133
|
+
spinner?.start();
|
|
10134
|
+
let cloned = runArgs("git", ["clone", repoUrl, "."], { cwd: DATA_DIR8, timeout: 300000 });
|
|
10135
|
+
if (!cloned) {
|
|
10136
|
+
const sshUrl = repoUrl.replace("https://github.com/", "git@github.com:");
|
|
10137
|
+
cloned = runArgs("git", ["clone", sshUrl, "."], { cwd: DATA_DIR8, timeout: 300000 });
|
|
10138
|
+
}
|
|
10139
|
+
if (cloned) {
|
|
10140
|
+
spinner?.succeed("Cloned into ~/Data");
|
|
10141
|
+
result.created.push(DATA_DIR8);
|
|
10142
|
+
} else {
|
|
10143
|
+
spinner?.fail("Clone failed");
|
|
10144
|
+
result.errors.push(`Could not clone from ${repoUrl}`);
|
|
10145
|
+
op.failStep("clone_repo", "Clone failed");
|
|
10146
|
+
op.fail("Clone failed");
|
|
10147
|
+
return result;
|
|
10148
|
+
}
|
|
10149
|
+
}
|
|
9217
10150
|
} else {
|
|
10151
|
+
if (isTTY) {
|
|
10152
|
+
console.log(` ${c2.yellow}⚠${c2.reset} ~/Data exists but is not a git repository`);
|
|
10153
|
+
console.log(` ${c2.dim}Expected a cloned Datacore repo. Some features may not work.${c2.reset}`);
|
|
10154
|
+
}
|
|
10155
|
+
result.warnings.push(`${DATA_DIR8} is not a git repository`);
|
|
10156
|
+
}
|
|
10157
|
+
const dipsDir = join10(DATACORE_DIR, "dips");
|
|
10158
|
+
if (existsSync9(DATACORE_DIR) && !existsSync9(join10(dipsDir, ".git"))) {
|
|
10159
|
+
const spinner = isTTY ? new Spinner("Fetching specifications...") : null;
|
|
10160
|
+
spinner?.start();
|
|
10161
|
+
const dipsRepo = "https://github.com/datacore-one/datacore-dips.git";
|
|
10162
|
+
let clonedDips = runArgs("git", ["clone", dipsRepo, dipsDir], { timeout: 300000 });
|
|
10163
|
+
if (!clonedDips) {
|
|
10164
|
+
clonedDips = runArgs("git", ["clone", "git@github.com:datacore-one/datacore-dips.git", dipsDir], { timeout: 300000 });
|
|
10165
|
+
}
|
|
10166
|
+
if (clonedDips) {
|
|
10167
|
+
spinner?.succeed("Specifications installed");
|
|
10168
|
+
} else {
|
|
10169
|
+
spinner?.fail("Could not fetch specifications (non-fatal)");
|
|
10170
|
+
result.warnings.push("Specifications repo not cloned");
|
|
10171
|
+
}
|
|
10172
|
+
} else if (existsSync9(join10(dipsDir, ".git"))) {
|
|
9218
10173
|
if (isTTY)
|
|
9219
|
-
console.log(` ${c2.green}✓${c2.reset}
|
|
10174
|
+
console.log(` ${c2.green}✓${c2.reset} Specifications present`);
|
|
10175
|
+
}
|
|
10176
|
+
if (commandExists3("git-lfs") && existsSync9(join10(DATA_DIR8, ".git"))) {
|
|
10177
|
+
const spinner = isTTY ? new Spinner("Initializing Git LFS...") : null;
|
|
10178
|
+
spinner?.start();
|
|
10179
|
+
if (runArgs("git", ["lfs", "install"], { cwd: DATA_DIR8, timeout: 30000 })) {
|
|
10180
|
+
runArgs("git", ["lfs", "pull"], { cwd: DATA_DIR8, timeout: 120000 });
|
|
10181
|
+
spinner?.succeed("Git LFS initialized");
|
|
10182
|
+
} else {
|
|
10183
|
+
spinner?.fail("Git LFS init failed (non-fatal)");
|
|
10184
|
+
}
|
|
9220
10185
|
}
|
|
9221
10186
|
if (isTTY)
|
|
9222
10187
|
console.log();
|
|
9223
|
-
op.completeStep("
|
|
9224
|
-
op.addStep("
|
|
9225
|
-
op.startStep("
|
|
10188
|
+
op.completeStep("clone_repo");
|
|
10189
|
+
op.addStep("team_spaces");
|
|
10190
|
+
op.startStep("team_spaces");
|
|
10191
|
+
if (interactive) {
|
|
10192
|
+
section(`Step 4/${TOTAL_STEPS}: Team Spaces`);
|
|
10193
|
+
console.log();
|
|
10194
|
+
console.log(` ${c2.dim}Spaces separate different areas of your life. Your personal space${c2.reset}`);
|
|
10195
|
+
console.log(` ${c2.dim}(0-personal) is created automatically. Team spaces are separate${c2.reset}`);
|
|
10196
|
+
console.log(` ${c2.dim}git repos for organizations you work with.${c2.reset}`);
|
|
10197
|
+
console.log();
|
|
10198
|
+
if (profile.useCase === "personal") {
|
|
10199
|
+
console.log(` ${c2.dim}You selected personal use. You can add team spaces anytime later:${c2.reset}`);
|
|
10200
|
+
console.log(` ${c2.dim}datacore space create <name>${c2.reset}`);
|
|
10201
|
+
} else {
|
|
10202
|
+
console.log(` ${c2.dim}Each team space gets its own:${c2.reset}`);
|
|
10203
|
+
console.log(` ${c2.dim}• GTD task system and AI agents${c2.reset}`);
|
|
10204
|
+
console.log(` ${c2.dim}• Knowledge base (wiki, notes, research)${c2.reset}`);
|
|
10205
|
+
console.log(` ${c2.dim}• Project tracking via GitHub Issues${c2.reset}`);
|
|
10206
|
+
console.log();
|
|
10207
|
+
const existingSpaces = listSpaces();
|
|
10208
|
+
const existingNames = existingSpaces.map((s) => s.name.replace(/^\d+-/, ""));
|
|
10209
|
+
if (existingSpaces.length > 1) {
|
|
10210
|
+
console.log(` ${c2.dim}Existing spaces:${c2.reset}`);
|
|
10211
|
+
for (const s of existingSpaces) {
|
|
10212
|
+
console.log(` ${s.type === "personal" ? "\uD83D\uDC64" : "\uD83D\uDC65"} ${s.name}`);
|
|
10213
|
+
}
|
|
10214
|
+
console.log();
|
|
10215
|
+
}
|
|
10216
|
+
const wantSpace = await confirm(" Would you like to add a team space?", false);
|
|
10217
|
+
if (wantSpace) {
|
|
10218
|
+
let adding = true;
|
|
10219
|
+
while (adding) {
|
|
10220
|
+
const spaceName = await prompt(' Space name (e.g., "datafund", "acme-corp")');
|
|
10221
|
+
if (!spaceName) {
|
|
10222
|
+
adding = false;
|
|
10223
|
+
continue;
|
|
10224
|
+
}
|
|
10225
|
+
const normalized = spaceName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
10226
|
+
const knownMatch = KNOWN_SPACES.find((ks) => ks.name === normalized || ks.displayName.toLowerCase() === spaceName.toLowerCase() || normalized.length >= 3 && ks.name.startsWith(normalized) || normalized.length >= 3 && ks.displayName.toLowerCase().startsWith(normalized.toLowerCase()));
|
|
10227
|
+
if (existingNames.includes(normalized) || existingNames.includes(knownMatch?.name ?? "")) {
|
|
10228
|
+
console.log(` ${c2.green}✓${c2.reset} ${spaceName} is already installed`);
|
|
10229
|
+
adding = await confirm(" Add another?", false);
|
|
10230
|
+
continue;
|
|
10231
|
+
}
|
|
10232
|
+
if (knownMatch) {
|
|
10233
|
+
console.log(` ${c2.green}✓${c2.reset} Found registered space: ${c2.bold}${knownMatch.displayName}${c2.reset}`);
|
|
10234
|
+
console.log(` ${c2.dim}${knownMatch.description}${c2.reset}`);
|
|
10235
|
+
const currentSpaces = listSpaces();
|
|
10236
|
+
const nextNum = currentSpaces.length > 0 ? Math.max(...currentSpaces.map((s) => s.number)) + 1 : 1;
|
|
10237
|
+
const spacePath = join10(DATA_DIR8, `${nextNum}-${knownMatch.name}`);
|
|
10238
|
+
const spinner = new Spinner(`Cloning ${knownMatch.displayName}...`);
|
|
10239
|
+
spinner.start();
|
|
10240
|
+
let cloned = runArgs("git", ["clone", knownMatch.repo, spacePath], { timeout: 300000 });
|
|
10241
|
+
if (!cloned) {
|
|
10242
|
+
const sshUrl = knownMatch.repo.replace("https://github.com/", "git@github.com:");
|
|
10243
|
+
cloned = runArgs("git", ["clone", sshUrl, spacePath], { timeout: 300000 });
|
|
10244
|
+
}
|
|
10245
|
+
if (cloned) {
|
|
10246
|
+
spinner.succeed(`Added space: ${nextNum}-${knownMatch.name}`);
|
|
10247
|
+
result.spacesCreated.push(`${nextNum}-${knownMatch.name}`);
|
|
10248
|
+
} else {
|
|
10249
|
+
spinner.fail(`Could not clone ${knownMatch.displayName}`);
|
|
10250
|
+
if (knownMatch.private) {
|
|
10251
|
+
console.log(` ${c2.dim}This is a private repo. Make sure you have access to ${knownMatch.org}.${c2.reset}`);
|
|
10252
|
+
console.log(` ${c2.dim}Request access or try: gh auth refresh -s read:org${c2.reset}`);
|
|
10253
|
+
}
|
|
10254
|
+
result.warnings.push(`Failed to add space: ${knownMatch.name}`);
|
|
10255
|
+
}
|
|
10256
|
+
} else {
|
|
10257
|
+
const repoUrl = await prompt(" Git repo URL (or Enter to create local)", "");
|
|
10258
|
+
if (repoUrl) {
|
|
10259
|
+
const currentSpaces = listSpaces();
|
|
10260
|
+
const nextNum = currentSpaces.length > 0 ? Math.max(...currentSpaces.map((s) => s.number)) + 1 : 1;
|
|
10261
|
+
const spacePath = join10(DATA_DIR8, `${nextNum}-${normalized}`);
|
|
10262
|
+
const spinner = new Spinner(`Cloning ${spaceName}...`);
|
|
10263
|
+
spinner.start();
|
|
10264
|
+
let cloned = runArgs("git", ["clone", repoUrl, spacePath], { timeout: 300000 });
|
|
10265
|
+
if (!cloned) {
|
|
10266
|
+
const sshUrl = repoUrl.replace("https://github.com/", "git@github.com:");
|
|
10267
|
+
cloned = runArgs("git", ["clone", sshUrl, spacePath], { timeout: 300000 });
|
|
10268
|
+
}
|
|
10269
|
+
if (cloned) {
|
|
10270
|
+
spinner.succeed(`Added space: ${nextNum}-${normalized}`);
|
|
10271
|
+
result.spacesCreated.push(`${nextNum}-${normalized}`);
|
|
10272
|
+
} else {
|
|
10273
|
+
spinner.fail(`Could not clone ${repoUrl}`);
|
|
10274
|
+
result.warnings.push(`Failed to add space: ${spaceName}`);
|
|
10275
|
+
}
|
|
10276
|
+
} else {
|
|
10277
|
+
try {
|
|
10278
|
+
const { createSpace: createSpace2 } = await Promise.resolve().then(() => (init_space(), exports_space));
|
|
10279
|
+
const space = createSpace2(spaceName, "team");
|
|
10280
|
+
result.created.push(space.path);
|
|
10281
|
+
result.spacesCreated.push(space.name);
|
|
10282
|
+
console.log(` ${c2.green}✓${c2.reset} Created ${space.name}/`);
|
|
10283
|
+
} catch (err) {
|
|
10284
|
+
console.log(` ${c2.red}✗${c2.reset} ${err.message}`);
|
|
10285
|
+
}
|
|
10286
|
+
}
|
|
10287
|
+
}
|
|
10288
|
+
adding = await confirm(" Add another?", false);
|
|
10289
|
+
}
|
|
10290
|
+
}
|
|
10291
|
+
}
|
|
10292
|
+
console.log();
|
|
10293
|
+
}
|
|
10294
|
+
op.completeStep("team_spaces");
|
|
10295
|
+
op.addStep("second_brain");
|
|
10296
|
+
op.startStep("second_brain");
|
|
9226
10297
|
if (isTTY) {
|
|
9227
|
-
section(
|
|
9228
|
-
console.log(
|
|
10298
|
+
section(`Step 5/${TOTAL_STEPS}: Your Second Brain`);
|
|
10299
|
+
console.log();
|
|
10300
|
+
console.log(` ${c2.dim}Your personal space is your AI-powered second brain. Here's how${c2.reset}`);
|
|
10301
|
+
console.log(` ${c2.dim}it's organized:${c2.reset}`);
|
|
10302
|
+
console.log();
|
|
10303
|
+
console.log(` ${c2.bold}Getting Things Done (GTD)${c2.reset}`);
|
|
10304
|
+
console.log(` ${c2.dim}A trusted system where you capture everything into an inbox, then${c2.reset}`);
|
|
10305
|
+
console.log(` ${c2.dim}process it into actionable next steps. Nothing stays in your head.${c2.reset}`);
|
|
10306
|
+
console.log(` ${c2.dim}• org/inbox.org → Capture anything, anytime${c2.reset}`);
|
|
10307
|
+
console.log(` ${c2.dim}• org/next_actions.org → What you're actually doing${c2.reset}`);
|
|
10308
|
+
console.log(` ${c2.dim}• org/someday.org → Ideas for later${c2.reset}`);
|
|
10309
|
+
console.log();
|
|
10310
|
+
console.log(` ${c2.bold}AI Delegation${c2.reset}`);
|
|
10311
|
+
console.log(` ${c2.dim}Tag tasks with :AI: and agents handle them overnight:${c2.reset}`);
|
|
10312
|
+
console.log(` ${c2.dim}• :AI:research: → Deep research on any topic${c2.reset}`);
|
|
10313
|
+
console.log(` ${c2.dim}• :AI:content: → Draft emails, blog posts, docs${c2.reset}`);
|
|
10314
|
+
console.log(` ${c2.dim}• :AI:data: → Analyze data, generate reports${c2.reset}`);
|
|
10315
|
+
console.log(` ${c2.dim}• :AI:pm: → Track projects, flag blockers${c2.reset}`);
|
|
10316
|
+
console.log();
|
|
10317
|
+
console.log(` ${c2.bold}Knowledge Management${c2.reset}`);
|
|
10318
|
+
console.log(` ${c2.dim}Your knowledge compounds over time. Every note, insight, and${c2.reset}`);
|
|
10319
|
+
console.log(` ${c2.dim}conversation gets woven into a personal knowledge base:${c2.reset}`);
|
|
10320
|
+
console.log(` ${c2.dim}• notes/ → Daily journals, quick captures${c2.reset}`);
|
|
10321
|
+
console.log(` ${c2.dim}• 3-knowledge/ → Permanent knowledge (Zettelkasten)${c2.reset}`);
|
|
10322
|
+
console.log(` ${c2.dim}├── zettel/ → Atomic ideas, one concept per note${c2.reset}`);
|
|
10323
|
+
console.log(` ${c2.dim}├── pages/ → Longer topic pages and guides${c2.reset}`);
|
|
10324
|
+
console.log(` ${c2.dim}├── literature/ → Summaries of things you've read${c2.reset}`);
|
|
10325
|
+
console.log(` ${c2.dim}└── reference/ → People, companies, glossary${c2.reset}`);
|
|
10326
|
+
console.log();
|
|
10327
|
+
console.log(` ${c2.dim}The more you capture, the smarter your system becomes. Agents${c2.reset}`);
|
|
10328
|
+
console.log(` ${c2.dim}cross-reference your knowledge to give better answers over time.${c2.reset}`);
|
|
9229
10329
|
console.log();
|
|
9230
10330
|
}
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
{
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
{
|
|
9239
|
-
|
|
9240
|
-
{
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
10331
|
+
const personalPath = join10(DATA_DIR8, "0-personal");
|
|
10332
|
+
if (existsSync9(personalPath)) {
|
|
10333
|
+
if (isTTY)
|
|
10334
|
+
console.log(` ${c2.green}✓${c2.reset} Personal space ready (0-personal/)`);
|
|
10335
|
+
result.spacesCreated.push("0-personal");
|
|
10336
|
+
} else {
|
|
10337
|
+
if (isTTY)
|
|
10338
|
+
console.log(` ${c2.dim}Creating personal space...${c2.reset}`);
|
|
10339
|
+
try {
|
|
10340
|
+
const { createSpace: createSpace2 } = await Promise.resolve().then(() => (init_space(), exports_space));
|
|
10341
|
+
const space = createSpace2("personal", "personal");
|
|
10342
|
+
result.created.push(space.path);
|
|
10343
|
+
result.spacesCreated.push("0-personal");
|
|
10344
|
+
if (isTTY)
|
|
10345
|
+
console.log(` ${c2.green}✓${c2.reset} Personal space created (0-personal/)`);
|
|
10346
|
+
} catch {
|
|
10347
|
+
const dirs = ["org", "notes", "notes/journals", "notes/pages", "journal", "3-knowledge", "content", "0-inbox"];
|
|
10348
|
+
for (const dir of dirs) {
|
|
10349
|
+
mkdirSync6(join10(personalPath, dir), { recursive: true });
|
|
9249
10350
|
}
|
|
10351
|
+
result.created.push(personalPath);
|
|
10352
|
+
result.spacesCreated.push("0-personal");
|
|
10353
|
+
if (isTTY)
|
|
10354
|
+
console.log(` ${c2.green}✓${c2.reset} Personal space initialized`);
|
|
9250
10355
|
}
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
10356
|
+
}
|
|
10357
|
+
const gtdFiles = ["inbox.org", "next_actions.org", "someday.org"];
|
|
10358
|
+
const orgDir = join10(personalPath, "org");
|
|
10359
|
+
let gtdReady = true;
|
|
10360
|
+
for (const file of gtdFiles) {
|
|
10361
|
+
if (!existsSync9(join10(orgDir, file)))
|
|
10362
|
+
gtdReady = false;
|
|
10363
|
+
}
|
|
10364
|
+
if (gtdReady) {
|
|
10365
|
+
if (isTTY)
|
|
10366
|
+
console.log(` ${c2.green}✓${c2.reset} GTD system initialized`);
|
|
10367
|
+
}
|
|
10368
|
+
if (existsSync9(join10(personalPath, "3-knowledge"))) {
|
|
10369
|
+
if (isTTY)
|
|
10370
|
+
console.log(` ${c2.green}✓${c2.reset} Knowledge base ready`);
|
|
10371
|
+
}
|
|
10372
|
+
const templates = [
|
|
10373
|
+
{ src: "install.yaml.example", dst: "install.yaml", label: "Installation manifest" },
|
|
10374
|
+
{ src: "0-personal/org/inbox.org.example", dst: "0-personal/org/inbox.org", label: "GTD inbox" },
|
|
10375
|
+
{ src: "0-personal/org/next_actions.org.example", dst: "0-personal/org/next_actions.org", label: "GTD next actions" },
|
|
10376
|
+
{ src: "0-personal/org/someday.org.example", dst: "0-personal/org/someday.org", label: "GTD someday" },
|
|
10377
|
+
{ src: "0-personal/org/habits.org.example", dst: "0-personal/org/habits.org", label: "GTD habits" }
|
|
10378
|
+
];
|
|
10379
|
+
for (const { src, dst } of templates) {
|
|
10380
|
+
const srcPath = join10(DATA_DIR8, src);
|
|
10381
|
+
const dstPath = join10(DATA_DIR8, dst);
|
|
10382
|
+
if (existsSync9(srcPath) && !existsSync9(dstPath)) {
|
|
10383
|
+
copyFileSync(srcPath, dstPath);
|
|
10384
|
+
result.created.push(dstPath);
|
|
10385
|
+
}
|
|
10386
|
+
}
|
|
10387
|
+
if (isTTY)
|
|
10388
|
+
console.log(` ${c2.green}✓${c2.reset} Templates activated`);
|
|
10389
|
+
const claudeLocalPath = join10(DATA_DIR8, "CLAUDE.local.md");
|
|
10390
|
+
if (!existsSync9(claudeLocalPath)) {
|
|
10391
|
+
const localContent = [
|
|
10392
|
+
`<!-- PRIVATE LAYER - This file is gitignored and never shared -->`,
|
|
10393
|
+
``,
|
|
10394
|
+
`# ${profile.name || "My"}'s Datacore`,
|
|
10395
|
+
``,
|
|
10396
|
+
profile.role ? `Role: ${profile.role}` : "",
|
|
10397
|
+
``,
|
|
10398
|
+
`## My Workflow`,
|
|
10399
|
+
``,
|
|
10400
|
+
`<!-- Add your personal workflow notes, preferences, and shortcuts here. -->`,
|
|
10401
|
+
`<!-- This file is gitignored and only visible to your local Claude Code. -->`,
|
|
10402
|
+
``,
|
|
10403
|
+
`## Custom Context`,
|
|
10404
|
+
``,
|
|
10405
|
+
`<!-- Any private context that helps Claude assist you better: -->`,
|
|
10406
|
+
`<!-- - Project abbreviations and shorthand -->`,
|
|
10407
|
+
`<!-- - Personal communication preferences -->`,
|
|
10408
|
+
`<!-- - Domain expertise and background -->`,
|
|
10409
|
+
``
|
|
10410
|
+
].filter(Boolean).join(`
|
|
9272
10411
|
`);
|
|
9273
|
-
|
|
9274
|
-
|
|
10412
|
+
writeFileSync5(claudeLocalPath, localContent);
|
|
10413
|
+
result.created.push(claudeLocalPath);
|
|
10414
|
+
if (isTTY)
|
|
10415
|
+
console.log(` ${c2.green}✓${c2.reset} CLAUDE.local.md created (your private AI context)`);
|
|
10416
|
+
}
|
|
10417
|
+
const settingsLocalPath = join10(DATACORE_DIR, "settings.local.yaml");
|
|
10418
|
+
if (existsSync9(DATACORE_DIR) && !existsSync9(settingsLocalPath)) {
|
|
10419
|
+
const settingsContent = [
|
|
10420
|
+
`# Personal Settings Overrides (gitignored)`,
|
|
10421
|
+
`# See settings.yaml for all available options`,
|
|
10422
|
+
``,
|
|
10423
|
+
`editor:`,
|
|
10424
|
+
` open_markdown_on_generate: false`,
|
|
10425
|
+
``,
|
|
10426
|
+
`sync:`,
|
|
10427
|
+
` pull_on_today: true`,
|
|
10428
|
+
` push_on_wrap_up: true`,
|
|
10429
|
+
``
|
|
10430
|
+
].join(`
|
|
9275
10431
|
`);
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
console.log(` ${c2.green}✓${c2.reset} Created settings.yaml`);
|
|
9279
|
-
console.log(` ${c2.dim}Customize in settings.local.yaml${c2.reset}`);
|
|
9280
|
-
}
|
|
9281
|
-
} else {
|
|
10432
|
+
writeFileSync5(settingsLocalPath, settingsContent);
|
|
10433
|
+
result.created.push(settingsLocalPath);
|
|
9282
10434
|
if (isTTY)
|
|
9283
|
-
console.log(` ${c2.green}✓${c2.reset}
|
|
10435
|
+
console.log(` ${c2.green}✓${c2.reset} settings.local.yaml created (your preferences)`);
|
|
9284
10436
|
}
|
|
9285
10437
|
if (isTTY)
|
|
9286
10438
|
console.log();
|
|
9287
|
-
op.completeStep("
|
|
9288
|
-
op.addStep("
|
|
9289
|
-
op.startStep("
|
|
10439
|
+
op.completeStep("second_brain");
|
|
10440
|
+
op.addStep("modules");
|
|
10441
|
+
op.startStep("modules");
|
|
9290
10442
|
if (isTTY) {
|
|
9291
|
-
section(
|
|
9292
|
-
console.log(
|
|
10443
|
+
section(`Step 6/${TOTAL_STEPS}: Modules`);
|
|
10444
|
+
console.log();
|
|
10445
|
+
console.log(` ${c2.dim}Modules extend Datacore with specialized capabilities. Each adds${c2.reset}`);
|
|
10446
|
+
console.log(` ${c2.dim}new AI agents and /commands you can use in Claude Code.${c2.reset}`);
|
|
9293
10447
|
console.log();
|
|
9294
10448
|
}
|
|
9295
|
-
const
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
console.log(`
|
|
10449
|
+
const modulesDir = join10(DATACORE_DIR, "modules");
|
|
10450
|
+
if (existsSync9(DATACORE_DIR)) {
|
|
10451
|
+
mkdirSync6(modulesDir, { recursive: true });
|
|
10452
|
+
}
|
|
10453
|
+
const installedNames = listModules().map((m) => m.name);
|
|
10454
|
+
const allModules = AVAILABLE_MODULES;
|
|
10455
|
+
let modulesToInstall = [];
|
|
10456
|
+
if (interactive) {
|
|
10457
|
+
console.log(` ${c2.dim}All modules are selected by default. Deselect any you don't need:${c2.reset}`);
|
|
10458
|
+
console.log();
|
|
10459
|
+
const selected = new Set(allModules.map((_, i) => i));
|
|
10460
|
+
for (let i = 0;i < allModules.length; i++) {
|
|
10461
|
+
const mod = allModules[i];
|
|
10462
|
+
const isInstalled = installedNames.includes(mod.name);
|
|
10463
|
+
const coreLabel = mod.core ? ` ${c2.dim}(core)${c2.reset}` : "";
|
|
10464
|
+
const installedLabel = isInstalled ? ` ${c2.dim}(installed)${c2.reset}` : "";
|
|
10465
|
+
const num = String(i + 1).padStart(2, " ");
|
|
10466
|
+
console.log(` [x] ${c2.cyan}${num}${c2.reset}. ${c2.bold}${mod.name.padEnd(12)}${c2.reset} ${mod.description}${coreLabel}${installedLabel}`);
|
|
10467
|
+
}
|
|
10468
|
+
console.log();
|
|
10469
|
+
const removeInput = await prompt(" Enter numbers to REMOVE, or press Enter to install all", "");
|
|
10470
|
+
if (removeInput) {
|
|
10471
|
+
const nums = removeInput.split(/[,\s]+/).map((s) => parseInt(s.trim(), 10));
|
|
10472
|
+
for (const n of nums) {
|
|
10473
|
+
if (n >= 1 && n <= allModules.length) {
|
|
10474
|
+
const mod = allModules[n - 1];
|
|
10475
|
+
if (!mod.core) {
|
|
10476
|
+
selected.delete(n - 1);
|
|
10477
|
+
}
|
|
10478
|
+
}
|
|
10479
|
+
}
|
|
9313
10480
|
}
|
|
10481
|
+
modulesToInstall = allModules.filter((_, i) => selected.has(i));
|
|
9314
10482
|
} else {
|
|
9315
|
-
|
|
9316
|
-
|
|
10483
|
+
modulesToInstall = [...allModules];
|
|
10484
|
+
}
|
|
10485
|
+
let installCount = 0;
|
|
10486
|
+
for (const mod of modulesToInstall) {
|
|
10487
|
+
if (installedNames.includes(mod.name)) {
|
|
10488
|
+
installCount++;
|
|
10489
|
+
continue;
|
|
10490
|
+
}
|
|
10491
|
+
const spinner = isTTY ? new Spinner(`Installing ${mod.name}...`) : null;
|
|
10492
|
+
spinner?.start();
|
|
10493
|
+
try {
|
|
10494
|
+
await sleep(100);
|
|
10495
|
+
const info2 = installModule(mod.repo);
|
|
10496
|
+
result.modulesInstalled.push(mod.name);
|
|
10497
|
+
installCount++;
|
|
10498
|
+
const postInstall = runModulePostInstall(info2.path);
|
|
10499
|
+
if (postInstall.ran && postInstall.success) {
|
|
10500
|
+
spinner?.succeed(`${mod.name} - dependencies installed (${postInstall.type})`);
|
|
10501
|
+
} else if (postInstall.ran && !postInstall.success) {
|
|
10502
|
+
spinner?.succeed(`${mod.name}`);
|
|
10503
|
+
if (isTTY)
|
|
10504
|
+
console.log(` ${c2.yellow}⚠${c2.reset} ${c2.dim}Dependency install failed (${postInstall.type})${c2.reset}`);
|
|
10505
|
+
} else {
|
|
10506
|
+
spinner?.succeed(mod.name);
|
|
10507
|
+
}
|
|
10508
|
+
} catch (err) {
|
|
10509
|
+
spinner?.fail(`${mod.name} - ${err.message}`);
|
|
10510
|
+
result.warnings.push(`Module ${mod.name} failed to install: ${err.message}`);
|
|
10511
|
+
}
|
|
10512
|
+
}
|
|
10513
|
+
if (isTTY) {
|
|
10514
|
+
console.log();
|
|
10515
|
+
console.log(` ${c2.green}${installCount} modules installed.${c2.reset}`);
|
|
10516
|
+
console.log();
|
|
10517
|
+
}
|
|
10518
|
+
op.completeStep("modules");
|
|
10519
|
+
op.addStep("finalize");
|
|
10520
|
+
op.startStep("finalize");
|
|
10521
|
+
if (isTTY) {
|
|
10522
|
+
section(`Step 7/${TOTAL_STEPS}: Finalize`);
|
|
10523
|
+
console.log();
|
|
10524
|
+
}
|
|
10525
|
+
const contextMerge = join10(DATACORE_DIR, "lib", "context_merge.py");
|
|
10526
|
+
if (existsSync9(contextMerge)) {
|
|
10527
|
+
const spinner = isTTY ? new Spinner("Building CLAUDE.md from layers...") : null;
|
|
10528
|
+
spinner?.start();
|
|
10529
|
+
if (runArgs("python3", [contextMerge, "rebuild", "--path", DATA_DIR8, "--all"])) {
|
|
10530
|
+
spinner?.succeed("CLAUDE.md built from layers (all spaces)");
|
|
10531
|
+
} else {
|
|
10532
|
+
spinner?.fail("CLAUDE.md build failed (can rebuild later)");
|
|
10533
|
+
result.warnings.push("Could not build CLAUDE.md");
|
|
10534
|
+
}
|
|
10535
|
+
}
|
|
10536
|
+
const zettelDb = join10(DATACORE_DIR, "lib", "zettel_db.py");
|
|
10537
|
+
if (existsSync9(zettelDb)) {
|
|
10538
|
+
const spinner = isTTY ? new Spinner("Initializing knowledge database...") : null;
|
|
10539
|
+
spinner?.start();
|
|
10540
|
+
if (runArgs("python3", [zettelDb, "init-all"], { cwd: DATA_DIR8 })) {
|
|
10541
|
+
spinner?.succeed("Knowledge database initialized");
|
|
10542
|
+
} else {
|
|
10543
|
+
spinner?.fail("Database init failed (non-fatal)");
|
|
10544
|
+
result.warnings.push("Database initialization failed");
|
|
10545
|
+
}
|
|
10546
|
+
}
|
|
10547
|
+
if (existsSync9(DATACORE_DIR)) {
|
|
10548
|
+
const stateDir = join10(DATACORE_DIR, "state");
|
|
10549
|
+
const envDir = join10(DATACORE_DIR, "env");
|
|
10550
|
+
mkdirSync6(stateDir, { recursive: true });
|
|
10551
|
+
mkdirSync6(envDir, { recursive: true });
|
|
10552
|
+
if (!existsSync9(join10(stateDir, ".gitkeep")))
|
|
10553
|
+
writeFileSync5(join10(stateDir, ".gitkeep"), "");
|
|
10554
|
+
if (!existsSync9(join10(envDir, ".gitkeep")))
|
|
10555
|
+
writeFileSync5(join10(envDir, ".gitkeep"), "");
|
|
10556
|
+
if (isTTY)
|
|
10557
|
+
console.log(` ${c2.green}✓${c2.reset} Runtime directories ready`);
|
|
10558
|
+
}
|
|
10559
|
+
const claudeDir = join10(DATA_DIR8, ".claude");
|
|
10560
|
+
if (!existsSync9(claudeDir) && existsSync9(DATACORE_DIR)) {
|
|
10561
|
+
try {
|
|
10562
|
+
symlinkSync(DATACORE_DIR, claudeDir);
|
|
10563
|
+
result.created.push(claudeDir);
|
|
10564
|
+
} catch {}
|
|
10565
|
+
}
|
|
10566
|
+
const syncScript = join10(DATA_DIR8, "sync");
|
|
10567
|
+
if (existsSync9(syncScript)) {
|
|
10568
|
+
if (platform2 !== "windows") {
|
|
10569
|
+
runArgs("chmod", ["+x", syncScript]);
|
|
10570
|
+
}
|
|
10571
|
+
if (isTTY)
|
|
10572
|
+
console.log(` ${c2.green}✓${c2.reset} Sync script configured`);
|
|
10573
|
+
}
|
|
10574
|
+
const installYaml = join10(DATA_DIR8, "install.yaml");
|
|
10575
|
+
if (existsSync9(installYaml) || existsSync9(DATACORE_DIR)) {
|
|
10576
|
+
try {
|
|
10577
|
+
const allSpaces = listSpaces();
|
|
10578
|
+
const allInstalledModules = listModules();
|
|
10579
|
+
const spacesYaml = allSpaces.filter((s) => s.type !== "personal").map((s) => ` ${s.name}:
|
|
10580
|
+
path: ${s.name}`).join(`
|
|
10581
|
+
`);
|
|
10582
|
+
const modulesYaml = allInstalledModules.map((m) => ` - ${m.name}`).join(`
|
|
10583
|
+
`);
|
|
10584
|
+
const yamlContent = [
|
|
10585
|
+
`# Datacore Installation Manifest`,
|
|
10586
|
+
`# Generated by: datacore init`,
|
|
10587
|
+
`# Date: ${new Date().toISOString().split("T")[0]}`,
|
|
10588
|
+
``,
|
|
10589
|
+
`meta:`,
|
|
10590
|
+
` name: "${profile.name ? `${profile.name}'s Datacore` : "My Datacore"}"`,
|
|
10591
|
+
` root: "${DATA_DIR8}"`,
|
|
10592
|
+
` version: 1.0.0`,
|
|
10593
|
+
profile.role ? ` role: "${profile.role}"` : null,
|
|
10594
|
+
` use_case: ${profile.useCase}`,
|
|
10595
|
+
``,
|
|
10596
|
+
`modules:`,
|
|
10597
|
+
modulesYaml || " []",
|
|
10598
|
+
``,
|
|
10599
|
+
`personal:`,
|
|
10600
|
+
` path: 0-personal`,
|
|
10601
|
+
``,
|
|
10602
|
+
`spaces:`,
|
|
10603
|
+
spacesYaml || " {}",
|
|
10604
|
+
``
|
|
10605
|
+
].filter((line) => line !== null).join(`
|
|
10606
|
+
`);
|
|
10607
|
+
writeFileSync5(installYaml, yamlContent);
|
|
10608
|
+
if (isTTY)
|
|
10609
|
+
console.log(` ${c2.green}✓${c2.reset} install.yaml saved`);
|
|
10610
|
+
} catch {
|
|
10611
|
+
result.warnings.push("Could not update install.yaml");
|
|
9317
10612
|
}
|
|
9318
10613
|
}
|
|
10614
|
+
try {
|
|
10615
|
+
const snapshot = createSnapshot();
|
|
10616
|
+
saveSnapshot(snapshot);
|
|
10617
|
+
if (isTTY)
|
|
10618
|
+
console.log(` ${c2.green}✓${c2.reset} Snapshot created (datacore.lock.yaml)`);
|
|
10619
|
+
} catch {
|
|
10620
|
+
result.warnings.push("Could not create snapshot");
|
|
10621
|
+
}
|
|
9319
10622
|
if (isTTY)
|
|
9320
10623
|
console.log();
|
|
9321
|
-
op.completeStep("
|
|
10624
|
+
op.completeStep("finalize");
|
|
10625
|
+
op.addStep("import_data");
|
|
10626
|
+
op.startStep("import_data");
|
|
10627
|
+
const backgroundJobs = [];
|
|
10628
|
+
const canBackgroundIngest = commandExists3("datacore") && commandExists3("claude");
|
|
9322
10629
|
if (interactive) {
|
|
9323
|
-
section(
|
|
9324
|
-
console.log(` ${c2.dim}Spaces separate different areas of your life (work, projects, teams).${c2.reset}`);
|
|
9325
|
-
console.log(` ${c2.dim}Each space is a separate git repo with its own GTD system.${c2.reset}`);
|
|
10630
|
+
section(`Step 8/${TOTAL_STEPS}: Import Your Data`);
|
|
9326
10631
|
console.log();
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
10632
|
+
console.log(` ${c2.dim}Your second brain works best when it has your existing knowledge.${c2.reset}`);
|
|
10633
|
+
console.log(` ${c2.dim}You can import data now or do it later with 'datacore ingest'.${c2.reset}`);
|
|
10634
|
+
console.log();
|
|
10635
|
+
let importing = true;
|
|
10636
|
+
let totalImported = 0;
|
|
10637
|
+
const inboxDir = join10(DATA_DIR8, "0-personal", "0-inbox");
|
|
10638
|
+
mkdirSync6(inboxDir, { recursive: true });
|
|
10639
|
+
const IMPORT_SKIP = 4;
|
|
10640
|
+
while (importing) {
|
|
10641
|
+
console.log(` ${c2.dim}Common sources to import:${c2.reset}`);
|
|
10642
|
+
console.log(` ${c2.cyan}1${c2.reset}) ChatGPT conversation exports (JSON)`);
|
|
10643
|
+
console.log(` ${c2.cyan}2${c2.reset}) Documents folder (PDFs, Word docs, markdown)`);
|
|
10644
|
+
console.log(` ${c2.cyan}3${c2.reset}) Existing notes (Obsidian, Notion export, etc.)`);
|
|
10645
|
+
console.log(` ${c2.cyan}4${c2.reset}) Skip for now`);
|
|
10646
|
+
console.log();
|
|
10647
|
+
const choice = await prompt(" What would you like to import?", "4");
|
|
10648
|
+
const choiceNum = parseInt(choice, 10);
|
|
10649
|
+
if (choiceNum === IMPORT_SKIP || !choice) {
|
|
10650
|
+
if (totalImported === 0) {
|
|
10651
|
+
console.log();
|
|
10652
|
+
console.log(` ${c2.dim}No problem! You can import data anytime:${c2.reset}`);
|
|
10653
|
+
console.log(` ${c2.dim}datacore ingest ~/path/to/files${c2.reset}`);
|
|
10654
|
+
console.log(` ${c2.dim}datacore ingest ~/Downloads/chatgpt-export.json${c2.reset}`);
|
|
9335
10655
|
}
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
10656
|
+
importing = false;
|
|
10657
|
+
continue;
|
|
10658
|
+
}
|
|
10659
|
+
if (choiceNum === 1) {
|
|
10660
|
+
const chatPath = await prompt(" Path to ChatGPT export");
|
|
10661
|
+
const chatResolved = chatPath?.startsWith("~") ? join10(process.env.HOME || "", chatPath.slice(1)) : chatPath;
|
|
10662
|
+
if (chatResolved && existsSync9(chatResolved)) {
|
|
10663
|
+
const resolvedPath = chatResolved;
|
|
10664
|
+
const spinner = new Spinner("Processing ChatGPT export...");
|
|
10665
|
+
spinner.start();
|
|
10666
|
+
try {
|
|
10667
|
+
const content = readFileSync5(resolvedPath, "utf-8");
|
|
10668
|
+
const data = JSON.parse(content);
|
|
10669
|
+
const count = Array.isArray(data) ? data.length : 1;
|
|
10670
|
+
const destName = `chatgpt-export-${Date.now()}.json`;
|
|
10671
|
+
const destPath = join10(inboxDir, destName);
|
|
10672
|
+
copyFileSync(resolvedPath, destPath);
|
|
10673
|
+
if (canBackgroundIngest) {
|
|
10674
|
+
const job = spawnBackground("datacore", ["ingest", destPath], "ingest");
|
|
10675
|
+
if (job) {
|
|
10676
|
+
backgroundJobs.push(job);
|
|
10677
|
+
const bgOp = startOperation("background-ingest", { pid: job.pid, logFile: job.logFile, path: destPath });
|
|
10678
|
+
bgOp.start();
|
|
10679
|
+
spinner.succeed(`Found ${count} conversations - queued for background processing`);
|
|
10680
|
+
} else {
|
|
10681
|
+
spinner.succeed(`Found ${count} conversations - copied to inbox`);
|
|
10682
|
+
console.log(` ${c2.dim}Process with: datacore ingest ${destPath}${c2.reset}`);
|
|
10683
|
+
}
|
|
10684
|
+
} else {
|
|
10685
|
+
spinner.succeed(`Found ${count} conversations - copied to inbox`);
|
|
10686
|
+
console.log(` ${c2.dim}Process with /ingest in Claude Code for full knowledge extraction${c2.reset}`);
|
|
10687
|
+
}
|
|
10688
|
+
totalImported += count;
|
|
10689
|
+
} catch {
|
|
10690
|
+
spinner.fail("Could not parse ChatGPT export");
|
|
10691
|
+
try {
|
|
10692
|
+
const fallbackDest = join10(inboxDir, basename5(resolvedPath));
|
|
10693
|
+
copyFileSync(resolvedPath, fallbackDest);
|
|
10694
|
+
if (canBackgroundIngest) {
|
|
10695
|
+
const job = spawnBackground("datacore", ["ingest", fallbackDest], "ingest");
|
|
10696
|
+
if (job) {
|
|
10697
|
+
backgroundJobs.push(job);
|
|
10698
|
+
const bgOp = startOperation("background-ingest", { pid: job.pid, logFile: job.logFile, path: fallbackDest });
|
|
10699
|
+
bgOp.start();
|
|
10700
|
+
console.log(` ${c2.dim}File queued for background processing${c2.reset}`);
|
|
10701
|
+
} else {
|
|
10702
|
+
console.log(` ${c2.dim}File copied to inbox for later processing${c2.reset}`);
|
|
10703
|
+
}
|
|
10704
|
+
} else {
|
|
10705
|
+
console.log(` ${c2.dim}File copied to inbox for later processing${c2.reset}`);
|
|
10706
|
+
}
|
|
10707
|
+
} catch {}
|
|
10708
|
+
}
|
|
10709
|
+
} else {
|
|
10710
|
+
console.log(` ${c2.yellow}⚠${c2.reset} File not found: ${chatPath}`);
|
|
10711
|
+
}
|
|
10712
|
+
} else if (choiceNum === 2 || choiceNum === 3) {
|
|
10713
|
+
const label = choiceNum === 2 ? "documents" : "notes";
|
|
10714
|
+
const sourcePath = await prompt(` Path to ${label}`);
|
|
10715
|
+
if (sourcePath) {
|
|
10716
|
+
const resolvedPath = sourcePath.startsWith("~") ? join10(process.env.HOME || "", sourcePath.slice(1)) : sourcePath;
|
|
10717
|
+
if (existsSync9(resolvedPath)) {
|
|
10718
|
+
const spinner = new Spinner(`Scanning ${resolvedPath}...`);
|
|
10719
|
+
spinner.start();
|
|
10720
|
+
const stats = statSync2(resolvedPath);
|
|
10721
|
+
if (stats.isDirectory()) {
|
|
10722
|
+
const { total, byExt } = countFiles(resolvedPath);
|
|
10723
|
+
const extSummary = Object.entries(byExt).sort(([, a], [, b]) => b - a).slice(0, 4).map(([ext, count]) => `${count} .${ext}`).join(", ");
|
|
10724
|
+
spinner.succeed(`Found ${total} files (${extSummary})`);
|
|
10725
|
+
const destDir = join10(inboxDir, basename5(resolvedPath));
|
|
10726
|
+
const copySpinner = new Spinner("Copying to inbox...");
|
|
10727
|
+
copySpinner.start();
|
|
10728
|
+
try {
|
|
10729
|
+
cpSync(resolvedPath, destDir, { recursive: true });
|
|
10730
|
+
if (canBackgroundIngest) {
|
|
10731
|
+
const job = spawnBackground("datacore", ["ingest", destDir], "ingest");
|
|
10732
|
+
if (job) {
|
|
10733
|
+
backgroundJobs.push(job);
|
|
10734
|
+
const bgOp = startOperation("background-ingest", { pid: job.pid, logFile: job.logFile, path: destDir });
|
|
10735
|
+
bgOp.start();
|
|
10736
|
+
copySpinner.succeed(`${total} files queued for background processing`);
|
|
10737
|
+
} else {
|
|
10738
|
+
copySpinner.succeed(`${total} files copied to inbox`);
|
|
10739
|
+
console.log(` ${c2.dim}Process with: datacore ingest ${destDir}${c2.reset}`);
|
|
10740
|
+
}
|
|
10741
|
+
} else {
|
|
10742
|
+
copySpinner.succeed(`${total} files copied to inbox`);
|
|
10743
|
+
console.log(` ${c2.dim}Process with /ingest in Claude Code for full knowledge extraction${c2.reset}`);
|
|
10744
|
+
}
|
|
10745
|
+
totalImported += total;
|
|
10746
|
+
} catch {
|
|
10747
|
+
copySpinner.fail("Copy failed");
|
|
10748
|
+
}
|
|
10749
|
+
} else {
|
|
10750
|
+
const singleDest = join10(inboxDir, basename5(resolvedPath));
|
|
10751
|
+
copyFileSync(resolvedPath, singleDest);
|
|
10752
|
+
if (canBackgroundIngest) {
|
|
10753
|
+
const job = spawnBackground("datacore", ["ingest", singleDest], "ingest");
|
|
10754
|
+
if (job) {
|
|
10755
|
+
backgroundJobs.push(job);
|
|
10756
|
+
const bgOp = startOperation("background-ingest", { pid: job.pid, logFile: job.logFile, path: singleDest });
|
|
10757
|
+
bgOp.start();
|
|
10758
|
+
spinner.succeed("File queued for background processing");
|
|
10759
|
+
} else {
|
|
10760
|
+
spinner.succeed("File copied to inbox");
|
|
10761
|
+
console.log(` ${c2.dim}Process with: datacore ingest ${singleDest}${c2.reset}`);
|
|
10762
|
+
}
|
|
10763
|
+
} else {
|
|
10764
|
+
spinner.succeed("File copied to inbox");
|
|
10765
|
+
}
|
|
10766
|
+
totalImported++;
|
|
10767
|
+
}
|
|
10768
|
+
} else {
|
|
10769
|
+
console.log(` ${c2.yellow}⚠${c2.reset} Path not found: ${sourcePath}`);
|
|
10770
|
+
}
|
|
9343
10771
|
}
|
|
9344
|
-
creating = await confirm("Create another space?", false);
|
|
9345
10772
|
}
|
|
10773
|
+
console.log();
|
|
10774
|
+
importing = await confirm(" Import more?", false);
|
|
10775
|
+
if (importing)
|
|
10776
|
+
console.log();
|
|
9346
10777
|
}
|
|
9347
10778
|
console.log();
|
|
9348
10779
|
}
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
10780
|
+
op.completeStep("import_data");
|
|
10781
|
+
op.addStep("verification");
|
|
10782
|
+
op.startStep("verification");
|
|
10783
|
+
const claudeAvailable = commandExists3("claude");
|
|
10784
|
+
if (claudeAvailable && isTTY) {
|
|
10785
|
+
section(`Step 9/${TOTAL_STEPS}: Verification`);
|
|
9352
10786
|
console.log();
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
const choices = await prompt('Enter numbers to install (comma-separated, or "all", or "none")');
|
|
9369
|
-
let toInstall = [];
|
|
9370
|
-
if (choices.toLowerCase() === "all") {
|
|
9371
|
-
toInstall = available.map((m) => m.name);
|
|
9372
|
-
} else if (choices && choices.toLowerCase() !== "none") {
|
|
9373
|
-
const nums = choices.split(",").map((s) => parseInt(s.trim(), 10));
|
|
9374
|
-
toInstall = nums.filter((n) => n >= 1 && n <= available.length).map((n) => available[n - 1].name);
|
|
10787
|
+
console.log(` ${c2.dim}Running AI verification to check everything is configured correctly...${c2.reset}`);
|
|
10788
|
+
console.log();
|
|
10789
|
+
const spinner = new Spinner("Claude Code structural integrity check...");
|
|
10790
|
+
spinner.start();
|
|
10791
|
+
try {
|
|
10792
|
+
const agentResult = await invokeAgent({
|
|
10793
|
+
agent: "structural-integrity",
|
|
10794
|
+
params: { mode: "report", scope: "all" }
|
|
10795
|
+
}, { cwd: DATA_DIR8, timeout: 120000 });
|
|
10796
|
+
if (agentResult.success) {
|
|
10797
|
+
spinner.succeed("All checks passed");
|
|
10798
|
+
} else {
|
|
10799
|
+
spinner.fail("Verification completed with issues");
|
|
10800
|
+
if (agentResult.error) {
|
|
10801
|
+
result.warnings.push(`Verification: ${agentResult.error}`);
|
|
9375
10802
|
}
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
10803
|
+
}
|
|
10804
|
+
} catch {
|
|
10805
|
+
spinner.fail("Verification skipped (Claude Code not responding)");
|
|
10806
|
+
result.warnings.push("Could not run verification - run /structural-integrity manually");
|
|
10807
|
+
}
|
|
10808
|
+
const allSpaces = listSpaces();
|
|
10809
|
+
const allInstalledModules = listModules();
|
|
10810
|
+
console.log();
|
|
10811
|
+
console.log(` ${c2.dim}Spaces: ${allSpaces.length} (${allSpaces.map((s) => s.name).join(", ")})${c2.reset}`);
|
|
10812
|
+
console.log(` ${c2.dim}Modules: ${allInstalledModules.length} installed${c2.reset}`);
|
|
10813
|
+
const inboxOrg = join10(DATA_DIR8, "0-personal", "org", "inbox.org");
|
|
10814
|
+
if (existsSync9(inboxOrg)) {
|
|
10815
|
+
try {
|
|
10816
|
+
const content = readFileSync5(inboxOrg, "utf-8");
|
|
10817
|
+
const todoCount = (content.match(/^\* TODO /gm) || []).length;
|
|
10818
|
+
if (todoCount > 0) {
|
|
10819
|
+
console.log(` ${c2.dim}GTD: ${todoCount} inbox items${c2.reset}`);
|
|
9391
10820
|
}
|
|
10821
|
+
} catch {}
|
|
10822
|
+
}
|
|
10823
|
+
const inboxDir = join10(DATA_DIR8, "0-personal", "0-inbox");
|
|
10824
|
+
if (existsSync9(inboxDir)) {
|
|
10825
|
+
const { total } = countFiles(inboxDir);
|
|
10826
|
+
if (total > 0) {
|
|
10827
|
+
console.log(` ${c2.dim}Import: ${total} files in inbox${c2.reset}`);
|
|
9392
10828
|
}
|
|
9393
|
-
} else {
|
|
9394
|
-
console.log(` ${c2.dim}No additional modules available to install.${c2.reset}`);
|
|
9395
10829
|
}
|
|
9396
10830
|
console.log();
|
|
9397
|
-
}
|
|
9398
|
-
|
|
9399
|
-
op.startStep("create_claude_symlink");
|
|
9400
|
-
if (isTTY) {
|
|
9401
|
-
section("Step 7: Claude Code Integration");
|
|
9402
|
-
console.log(` ${c2.dim}Connecting Datacore to Claude Code AI assistant.${c2.reset}`);
|
|
10831
|
+
} else if (isTTY) {
|
|
10832
|
+
section(`Step 9/${TOTAL_STEPS}: Verification`);
|
|
9403
10833
|
console.log();
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
if (!existsSync7(claudeDir)) {
|
|
9407
|
-
try {
|
|
9408
|
-
symlinkSync(DATACORE_DIR, claudeDir);
|
|
9409
|
-
result.created.push(claudeDir);
|
|
9410
|
-
if (isTTY)
|
|
9411
|
-
console.log(` ${c2.green}✓${c2.reset} Created .claude -> .datacore symlink`);
|
|
9412
|
-
} catch {
|
|
9413
|
-
result.warnings.push("Could not create .claude symlink");
|
|
9414
|
-
if (isTTY)
|
|
9415
|
-
console.log(` ${c2.yellow}○${c2.reset} Could not create symlink`);
|
|
9416
|
-
}
|
|
9417
|
-
} else {
|
|
9418
|
-
if (isTTY)
|
|
9419
|
-
console.log(` ${c2.green}✓${c2.reset} Found existing .claude/`);
|
|
9420
|
-
}
|
|
9421
|
-
const claudeMd = join7(DATA_DIR5, "CLAUDE.md");
|
|
9422
|
-
const claudeBaseMd = join7(DATA_DIR5, "CLAUDE.base.md");
|
|
9423
|
-
if (!existsSync7(claudeMd) && !existsSync7(claudeBaseMd)) {
|
|
9424
|
-
const allSpaces = listSpaces();
|
|
9425
|
-
writeFileSync4(claudeBaseMd, `# Datacore
|
|
9426
|
-
|
|
9427
|
-
AI-powered second brain built on GTD methodology.
|
|
9428
|
-
|
|
9429
|
-
## Quick Start
|
|
9430
|
-
|
|
9431
|
-
\`\`\`bash
|
|
9432
|
-
cd ~/Data && claude
|
|
9433
|
-
\`\`\`
|
|
9434
|
-
|
|
9435
|
-
## Daily Workflow
|
|
9436
|
-
|
|
9437
|
-
1. **Morning**: Run \`/today\` for your daily briefing
|
|
9438
|
-
2. **Capture**: Add tasks to \`0-personal/org/inbox.org\`
|
|
9439
|
-
3. **Process**: Clear inbox, organize by context
|
|
9440
|
-
4. **Work**: Use \`:AI:\` tags to delegate tasks
|
|
9441
|
-
5. **Evening**: Run \`/tomorrow\` for wrap-up
|
|
9442
|
-
|
|
9443
|
-
## Commands
|
|
9444
|
-
|
|
9445
|
-
- \`/today\` - Daily briefing with priorities
|
|
9446
|
-
- \`/tomorrow\` - End-of-day wrap-up
|
|
9447
|
-
- \`/sync\` - Sync all repos
|
|
9448
|
-
- \`datacore doctor\` - Check system health
|
|
9449
|
-
|
|
9450
|
-
## Spaces
|
|
9451
|
-
|
|
9452
|
-
${allSpaces.map((s) => `- ${s.name}`).join(`
|
|
9453
|
-
`) || "- 0-personal"}
|
|
9454
|
-
|
|
9455
|
-
## Documentation
|
|
9456
|
-
|
|
9457
|
-
See .datacore/specs/ for detailed documentation.
|
|
9458
|
-
`);
|
|
9459
|
-
result.created.push(claudeBaseMd);
|
|
9460
|
-
if (isTTY)
|
|
9461
|
-
console.log(` ${c2.green}✓${c2.reset} Created CLAUDE.base.md`);
|
|
9462
|
-
} else {
|
|
9463
|
-
if (isTTY)
|
|
9464
|
-
console.log(` ${c2.green}✓${c2.reset} Found existing CLAUDE.md`);
|
|
9465
|
-
}
|
|
9466
|
-
if (isTTY)
|
|
10834
|
+
console.log(` ${c2.yellow}○${c2.reset} Claude Code not available ${c2.dim}(skipping verification)${c2.reset}`);
|
|
10835
|
+
console.log(` ${c2.dim}Run /structural-integrity in Claude Code to verify later.${c2.reset}`);
|
|
9467
10836
|
console.log();
|
|
9468
|
-
|
|
10837
|
+
}
|
|
10838
|
+
op.completeStep("verification");
|
|
9469
10839
|
result.success = true;
|
|
9470
10840
|
result.nextSteps = [
|
|
9471
|
-
|
|
9472
|
-
"
|
|
9473
|
-
"
|
|
10841
|
+
`cd ${DATA_DIR8} && claude`,
|
|
10842
|
+
"Run /today for your first daily briefing",
|
|
10843
|
+
"Process inbox with /gtd-daily-start",
|
|
10844
|
+
"Run datacore doctor to check system health"
|
|
9474
10845
|
];
|
|
9475
10846
|
if (isTTY) {
|
|
9476
10847
|
console.log(INIT_COMPLETE);
|
|
9477
10848
|
console.log();
|
|
9478
|
-
console.log(` ${c2.bold}Setup Complete!${c2.reset}`);
|
|
10849
|
+
console.log(` ${c2.bold}Setup Complete${profile.name ? `, ${profile.name}` : ""}!${c2.reset}`);
|
|
9479
10850
|
console.log();
|
|
9480
|
-
|
|
9481
|
-
|
|
10851
|
+
const allSpaces = listSpaces();
|
|
10852
|
+
if (allSpaces.length > 0) {
|
|
10853
|
+
console.log(` ${c2.green}Your Datacore:${c2.reset}`);
|
|
10854
|
+
for (const s of allSpaces) {
|
|
10855
|
+
const icon = s.type === "personal" ? "\uD83D\uDC64" : "\uD83D\uDC65";
|
|
10856
|
+
console.log(` ${icon} ${s.name}`);
|
|
10857
|
+
}
|
|
10858
|
+
console.log();
|
|
9482
10859
|
}
|
|
9483
|
-
|
|
9484
|
-
|
|
10860
|
+
const installedModules = listModules();
|
|
10861
|
+
if (installedModules.length > 0) {
|
|
10862
|
+
console.log(` ${c2.green}Modules:${c2.reset} ${installedModules.map((m) => m.name).join(", ")}`);
|
|
10863
|
+
console.log();
|
|
10864
|
+
}
|
|
10865
|
+
const envDir = join10(DATACORE_DIR, "env");
|
|
10866
|
+
const envFiles = existsSync9(envDir) ? readdirSync3(envDir).filter((f) => f !== ".gitkeep") : [];
|
|
10867
|
+
if (envFiles.length === 0) {
|
|
10868
|
+
console.log(` ${c2.bold}API Keys:${c2.reset}`);
|
|
10869
|
+
console.log(` ${c2.dim}Some modules need API keys to function. Configure them in Claude Code:${c2.reset}`);
|
|
10870
|
+
console.log(` ${c2.dim}cd ~/Data && claude${c2.reset}`);
|
|
10871
|
+
console.log(` ${c2.dim}"Help me set up my API keys"${c2.reset}`);
|
|
10872
|
+
console.log(` ${c2.dim}Keys are stored in .datacore/env/ - gitignored, never leave your machine.${c2.reset}`);
|
|
10873
|
+
console.log();
|
|
9485
10874
|
}
|
|
9486
10875
|
if (result.warnings.length > 0) {
|
|
9487
10876
|
console.log(` ${c2.yellow}Warnings:${c2.reset} ${result.warnings.length}`);
|
|
10877
|
+
for (const w of result.warnings) {
|
|
10878
|
+
console.log(` ${c2.dim}• ${w}${c2.reset}`);
|
|
10879
|
+
}
|
|
10880
|
+
console.log();
|
|
9488
10881
|
}
|
|
9489
|
-
console.log();
|
|
9490
10882
|
console.log(` ${c2.bold}Get Started:${c2.reset}`);
|
|
9491
10883
|
console.log();
|
|
9492
10884
|
console.log(` ${c2.cyan}1.${c2.reset} cd ~/Data && claude`);
|
|
9493
10885
|
console.log(` ${c2.dim}Start Claude Code in your Datacore directory${c2.reset}`);
|
|
9494
10886
|
console.log();
|
|
9495
10887
|
console.log(` ${c2.cyan}2.${c2.reset} Type ${c2.cyan}/today${c2.reset}`);
|
|
9496
|
-
console.log(` ${c2.dim}Get your first daily briefing${c2.reset}`);
|
|
9497
|
-
console.log();
|
|
9498
|
-
console.log(` ${c2.cyan}3.${c2.reset} Add tasks to ${c2.cyan}0-personal/org/inbox.org${c2.reset}`);
|
|
9499
|
-
console.log(` ${c2.dim}Your GTD capture inbox${c2.reset}`);
|
|
10888
|
+
console.log(` ${c2.dim}Get your first daily briefing with your imported data${c2.reset}`);
|
|
9500
10889
|
console.log();
|
|
10890
|
+
const inboxDir = join10(DATA_DIR8, "0-personal", "0-inbox");
|
|
10891
|
+
const inboxFileCount = existsSync9(inboxDir) ? countFiles(inboxDir).total : 0;
|
|
10892
|
+
if (inboxFileCount > 0) {
|
|
10893
|
+
console.log(` ${c2.cyan}3.${c2.reset} Process your imports`);
|
|
10894
|
+
console.log(` ${c2.dim}${inboxFileCount} files in inbox - run /ingest in Claude Code${c2.reset}`);
|
|
10895
|
+
console.log();
|
|
10896
|
+
} else {
|
|
10897
|
+
console.log(` ${c2.cyan}3.${c2.reset} Process your inbox`);
|
|
10898
|
+
console.log(` ${c2.dim}Add tasks to org/inbox.org - try /gtd-daily-start${c2.reset}`);
|
|
10899
|
+
console.log();
|
|
10900
|
+
}
|
|
10901
|
+
if (backgroundJobs.length > 0) {
|
|
10902
|
+
console.log(` ${c2.green}Background imports:${c2.reset}`);
|
|
10903
|
+
for (const job of backgroundJobs) {
|
|
10904
|
+
if (verbose) {
|
|
10905
|
+
console.log(` ${c2.green}✓${c2.reset} ${job.label} processing (PID ${job.pid})`);
|
|
10906
|
+
console.log(` ${c2.dim}Log: ${job.logFile}${c2.reset}`);
|
|
10907
|
+
} else {
|
|
10908
|
+
console.log(` ${c2.green}✓${c2.reset} ${job.label} processing`);
|
|
10909
|
+
}
|
|
10910
|
+
}
|
|
10911
|
+
console.log(` ${c2.dim}Check progress: datacore status${c2.reset}`);
|
|
10912
|
+
console.log();
|
|
10913
|
+
}
|
|
10914
|
+
console.log(` ${c2.dim}Edit ~/Data/CLAUDE.local.md to teach Claude about you.${c2.reset}`);
|
|
9501
10915
|
console.log(` ${c2.dim}Run 'datacore doctor' anytime to check system health.${c2.reset}`);
|
|
9502
10916
|
console.log();
|
|
9503
10917
|
}
|
|
@@ -9509,233 +10923,8 @@ See .datacore/specs/ for detailed documentation.
|
|
|
9509
10923
|
return result;
|
|
9510
10924
|
}
|
|
9511
10925
|
|
|
9512
|
-
// src/lib/snapshot.ts
|
|
9513
|
-
var import_yaml2 = __toESM(require_dist(), 1);
|
|
9514
|
-
import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
9515
|
-
import { execSync as execSync5 } from "child_process";
|
|
9516
|
-
import { join as join8 } from "path";
|
|
9517
|
-
var DATA_DIR6 = join8(process.env.HOME || "~", "Data");
|
|
9518
|
-
var LOCK_FILE = join8(DATA_DIR6, "datacore.lock.yaml");
|
|
9519
|
-
var CLI_VERSION = "1.0.5";
|
|
9520
|
-
function getGitInfo(path) {
|
|
9521
|
-
if (!existsSync8(join8(path, ".git"))) {
|
|
9522
|
-
return {};
|
|
9523
|
-
}
|
|
9524
|
-
try {
|
|
9525
|
-
const remote = execSync5("git remote get-url origin 2>/dev/null || true", {
|
|
9526
|
-
cwd: path,
|
|
9527
|
-
encoding: "utf-8"
|
|
9528
|
-
}).trim() || undefined;
|
|
9529
|
-
const commit = execSync5("git rev-parse HEAD 2>/dev/null || true", {
|
|
9530
|
-
cwd: path,
|
|
9531
|
-
encoding: "utf-8"
|
|
9532
|
-
}).trim() || undefined;
|
|
9533
|
-
const branch = execSync5("git branch --show-current 2>/dev/null || true", {
|
|
9534
|
-
cwd: path,
|
|
9535
|
-
encoding: "utf-8"
|
|
9536
|
-
}).trim() || undefined;
|
|
9537
|
-
return { remote, commit, branch };
|
|
9538
|
-
} catch {
|
|
9539
|
-
return {};
|
|
9540
|
-
}
|
|
9541
|
-
}
|
|
9542
|
-
function createSnapshot(options = {}) {
|
|
9543
|
-
const { includeSettings = false } = options;
|
|
9544
|
-
const modules = [];
|
|
9545
|
-
for (const mod of listModules()) {
|
|
9546
|
-
const gitInfo = getGitInfo(mod.path);
|
|
9547
|
-
modules.push({
|
|
9548
|
-
name: mod.name,
|
|
9549
|
-
source: gitInfo.remote || "local",
|
|
9550
|
-
version: mod.version,
|
|
9551
|
-
commit: gitInfo.commit,
|
|
9552
|
-
branch: gitInfo.branch
|
|
9553
|
-
});
|
|
9554
|
-
}
|
|
9555
|
-
const spaces = [];
|
|
9556
|
-
for (const space of listSpaces()) {
|
|
9557
|
-
const gitInfo = getGitInfo(space.path);
|
|
9558
|
-
spaces.push({
|
|
9559
|
-
name: space.name,
|
|
9560
|
-
number: space.number,
|
|
9561
|
-
type: space.type,
|
|
9562
|
-
source: gitInfo.remote,
|
|
9563
|
-
commit: gitInfo.commit
|
|
9564
|
-
});
|
|
9565
|
-
}
|
|
9566
|
-
const deps = checkDependencies();
|
|
9567
|
-
const dependencies = deps.filter((d) => d.installed && d.version).map((d) => ({
|
|
9568
|
-
name: d.name,
|
|
9569
|
-
version: d.version,
|
|
9570
|
-
required: d.required
|
|
9571
|
-
}));
|
|
9572
|
-
const snapshot = {
|
|
9573
|
-
version: "1.0",
|
|
9574
|
-
created: new Date().toISOString(),
|
|
9575
|
-
cliVersion: CLI_VERSION,
|
|
9576
|
-
platform: `${process.platform}-${process.arch}`,
|
|
9577
|
-
modules,
|
|
9578
|
-
spaces,
|
|
9579
|
-
dependencies
|
|
9580
|
-
};
|
|
9581
|
-
if (includeSettings) {
|
|
9582
|
-
const settingsPath = join8(DATA_DIR6, ".datacore", "settings.yaml");
|
|
9583
|
-
if (existsSync8(settingsPath)) {
|
|
9584
|
-
try {
|
|
9585
|
-
const content = readFileSync4(settingsPath, "utf-8");
|
|
9586
|
-
snapshot.settings = import_yaml2.parse(content);
|
|
9587
|
-
} catch {}
|
|
9588
|
-
}
|
|
9589
|
-
}
|
|
9590
|
-
return snapshot;
|
|
9591
|
-
}
|
|
9592
|
-
function saveSnapshot(snapshot, path) {
|
|
9593
|
-
const lockPath = path || LOCK_FILE;
|
|
9594
|
-
const content = import_yaml2.stringify(snapshot, {
|
|
9595
|
-
lineWidth: 0
|
|
9596
|
-
});
|
|
9597
|
-
writeFileSync5(lockPath, content);
|
|
9598
|
-
return lockPath;
|
|
9599
|
-
}
|
|
9600
|
-
function loadSnapshot(path) {
|
|
9601
|
-
const lockPath = path || LOCK_FILE;
|
|
9602
|
-
if (!existsSync8(lockPath)) {
|
|
9603
|
-
return null;
|
|
9604
|
-
}
|
|
9605
|
-
try {
|
|
9606
|
-
const content = readFileSync4(lockPath, "utf-8");
|
|
9607
|
-
return import_yaml2.parse(content);
|
|
9608
|
-
} catch {
|
|
9609
|
-
return null;
|
|
9610
|
-
}
|
|
9611
|
-
}
|
|
9612
|
-
function diffSnapshot(snapshot) {
|
|
9613
|
-
const current = createSnapshot();
|
|
9614
|
-
const diff = {
|
|
9615
|
-
modules: { added: [], removed: [], changed: [] },
|
|
9616
|
-
spaces: { added: [], removed: [] },
|
|
9617
|
-
dependencies: { changed: [] }
|
|
9618
|
-
};
|
|
9619
|
-
const currentModules = new Map(current.modules.map((m) => [m.name, m]));
|
|
9620
|
-
const snapshotModules = new Map(snapshot.modules.map((m) => [m.name, m]));
|
|
9621
|
-
for (const [name, mod] of currentModules) {
|
|
9622
|
-
if (!snapshotModules.has(name)) {
|
|
9623
|
-
diff.modules.added.push(name);
|
|
9624
|
-
} else {
|
|
9625
|
-
const expected = snapshotModules.get(name);
|
|
9626
|
-
if (expected.commit && mod.commit && expected.commit !== mod.commit) {
|
|
9627
|
-
diff.modules.changed.push({
|
|
9628
|
-
name,
|
|
9629
|
-
from: expected.commit.slice(0, 7),
|
|
9630
|
-
to: mod.commit.slice(0, 7)
|
|
9631
|
-
});
|
|
9632
|
-
}
|
|
9633
|
-
}
|
|
9634
|
-
}
|
|
9635
|
-
for (const name of snapshotModules.keys()) {
|
|
9636
|
-
if (!currentModules.has(name)) {
|
|
9637
|
-
diff.modules.removed.push(name);
|
|
9638
|
-
}
|
|
9639
|
-
}
|
|
9640
|
-
const currentSpaces = new Set(current.spaces.map((s) => s.name));
|
|
9641
|
-
const snapshotSpaces = new Set(snapshot.spaces.map((s) => s.name));
|
|
9642
|
-
for (const name of currentSpaces) {
|
|
9643
|
-
if (!snapshotSpaces.has(name)) {
|
|
9644
|
-
diff.spaces.added.push(name);
|
|
9645
|
-
}
|
|
9646
|
-
}
|
|
9647
|
-
for (const name of snapshotSpaces) {
|
|
9648
|
-
if (!currentSpaces.has(name)) {
|
|
9649
|
-
diff.spaces.removed.push(name);
|
|
9650
|
-
}
|
|
9651
|
-
}
|
|
9652
|
-
const currentDeps = new Map(current.dependencies.map((d) => [d.name, d.version]));
|
|
9653
|
-
for (const dep of snapshot.dependencies) {
|
|
9654
|
-
const actualVersion = currentDeps.get(dep.name);
|
|
9655
|
-
if (actualVersion && actualVersion !== dep.version) {
|
|
9656
|
-
diff.dependencies.changed.push({
|
|
9657
|
-
name: dep.name,
|
|
9658
|
-
expected: dep.version,
|
|
9659
|
-
actual: actualVersion
|
|
9660
|
-
});
|
|
9661
|
-
}
|
|
9662
|
-
}
|
|
9663
|
-
return diff;
|
|
9664
|
-
}
|
|
9665
|
-
function restoreFromSnapshot(snapshot, options = {}) {
|
|
9666
|
-
const { modules = true, spaces = true, dryRun = false } = options;
|
|
9667
|
-
const result = {
|
|
9668
|
-
modulesInstalled: [],
|
|
9669
|
-
modulesFailed: [],
|
|
9670
|
-
spacesCreated: [],
|
|
9671
|
-
warnings: []
|
|
9672
|
-
};
|
|
9673
|
-
if (modules) {
|
|
9674
|
-
const currentModules = new Set(listModules().map((m) => m.name));
|
|
9675
|
-
for (const mod of snapshot.modules) {
|
|
9676
|
-
if (currentModules.has(mod.name)) {
|
|
9677
|
-
continue;
|
|
9678
|
-
}
|
|
9679
|
-
if (mod.source === "local") {
|
|
9680
|
-
result.warnings.push(`Module ${mod.name} was local, cannot restore`);
|
|
9681
|
-
continue;
|
|
9682
|
-
}
|
|
9683
|
-
if (dryRun) {
|
|
9684
|
-
result.modulesInstalled.push(mod.name);
|
|
9685
|
-
continue;
|
|
9686
|
-
}
|
|
9687
|
-
try {
|
|
9688
|
-
const modulesDir = join8(DATA_DIR6, ".datacore", "modules");
|
|
9689
|
-
if (!existsSync8(modulesDir)) {
|
|
9690
|
-
mkdirSync5(modulesDir, { recursive: true });
|
|
9691
|
-
}
|
|
9692
|
-
const modulePath = join8(modulesDir, mod.name);
|
|
9693
|
-
execSync5(`git clone ${mod.source} "${modulePath}"`, { stdio: "pipe" });
|
|
9694
|
-
if (mod.commit) {
|
|
9695
|
-
execSync5(`git checkout ${mod.commit}`, { cwd: modulePath, stdio: "pipe" });
|
|
9696
|
-
} else if (mod.branch) {
|
|
9697
|
-
execSync5(`git checkout ${mod.branch}`, { cwd: modulePath, stdio: "pipe" });
|
|
9698
|
-
}
|
|
9699
|
-
result.modulesInstalled.push(mod.name);
|
|
9700
|
-
} catch (err) {
|
|
9701
|
-
result.modulesFailed.push({
|
|
9702
|
-
name: mod.name,
|
|
9703
|
-
error: err.message
|
|
9704
|
-
});
|
|
9705
|
-
}
|
|
9706
|
-
}
|
|
9707
|
-
}
|
|
9708
|
-
if (spaces) {
|
|
9709
|
-
const currentSpaces = new Set(listSpaces().map((s) => s.name));
|
|
9710
|
-
for (const space of snapshot.spaces) {
|
|
9711
|
-
if (currentSpaces.has(space.name)) {
|
|
9712
|
-
continue;
|
|
9713
|
-
}
|
|
9714
|
-
if (dryRun) {
|
|
9715
|
-
result.spacesCreated.push(space.name);
|
|
9716
|
-
continue;
|
|
9717
|
-
}
|
|
9718
|
-
if (space.source) {
|
|
9719
|
-
try {
|
|
9720
|
-
const spacePath = join8(DATA_DIR6, space.name);
|
|
9721
|
-
execSync5(`git clone ${space.source} "${spacePath}"`, { stdio: "pipe" });
|
|
9722
|
-
if (space.commit) {
|
|
9723
|
-
execSync5(`git checkout ${space.commit}`, { cwd: spacePath, stdio: "pipe" });
|
|
9724
|
-
}
|
|
9725
|
-
result.spacesCreated.push(space.name);
|
|
9726
|
-
} catch (err) {
|
|
9727
|
-
result.warnings.push(`Could not clone space ${space.name}: ${err.message}`);
|
|
9728
|
-
}
|
|
9729
|
-
} else {
|
|
9730
|
-
result.warnings.push(`Space ${space.name} has no git source, skipping`);
|
|
9731
|
-
}
|
|
9732
|
-
}
|
|
9733
|
-
}
|
|
9734
|
-
return result;
|
|
9735
|
-
}
|
|
9736
|
-
|
|
9737
10926
|
// src/index.ts
|
|
9738
|
-
var VERSION = "1.0.
|
|
10927
|
+
var VERSION = "1.0.7";
|
|
9739
10928
|
var args = process.argv.slice(2);
|
|
9740
10929
|
var parsed = parseArgs(args);
|
|
9741
10930
|
async function handleMeta(command, cmdArgs, flags, format) {
|
|
@@ -9746,17 +10935,20 @@ async function handleMeta(command, cmdArgs, flags, format) {
|
|
|
9746
10935
|
case "init": {
|
|
9747
10936
|
if (isInitialized() && !flags.force) {
|
|
9748
10937
|
if (format === "json") {
|
|
9749
|
-
output({ success: true, message: "Datacore already initialized" }, format);
|
|
10938
|
+
output({ success: true, message: "Datacore already initialized", hint: "Use --force to re-initialize or run datacore doctor" }, format);
|
|
9750
10939
|
} else {
|
|
9751
|
-
info("Datacore already initialized");
|
|
9752
|
-
info("
|
|
10940
|
+
info("Datacore already initialized at ~/Data");
|
|
10941
|
+
info("Run datacore doctor to check health");
|
|
10942
|
+
info("Run datacore module list to see modules");
|
|
10943
|
+
info("Use --force to re-run the setup wizard");
|
|
9753
10944
|
}
|
|
9754
10945
|
break;
|
|
9755
10946
|
}
|
|
9756
10947
|
const result = await initDatacore({
|
|
9757
10948
|
nonInteractive: flags.yes === true,
|
|
9758
10949
|
skipChecks: flags["skip-checks"] === true,
|
|
9759
|
-
stream: format === "human"
|
|
10950
|
+
stream: format === "human",
|
|
10951
|
+
verbose: flags.verbose === true
|
|
9760
10952
|
});
|
|
9761
10953
|
if (format === "json") {
|
|
9762
10954
|
output(result, format);
|
|
@@ -9809,7 +11001,7 @@ async function handleMeta(command, cmdArgs, flags, format) {
|
|
|
9809
11001
|
for (const dep of result.dependencies) {
|
|
9810
11002
|
const status = dep.installed ? "✓" : "✗";
|
|
9811
11003
|
const version = dep.version ? ` (${dep.version})` : "";
|
|
9812
|
-
const required = dep.required ? "" : " (
|
|
11004
|
+
const required = dep.required ? "" : " (recommended)";
|
|
9813
11005
|
console.log(` ${status} ${dep.name}${version}${required}`);
|
|
9814
11006
|
if (!dep.installed && dep.installCommand) {
|
|
9815
11007
|
console.log(` Install: ${dep.installCommand}`);
|
|
@@ -10166,8 +11358,8 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10166
11358
|
throw new CLIError("ERR_INVALID_ARGUMENT", "Missing task description", 'Usage: datacore nightshift queue "Research topic X"');
|
|
10167
11359
|
}
|
|
10168
11360
|
const { existsSync: existsSync10, appendFileSync } = await import("fs");
|
|
10169
|
-
const { join:
|
|
10170
|
-
const inboxPath =
|
|
11361
|
+
const { join: join11 } = await import("path");
|
|
11362
|
+
const inboxPath = join11(process.env.HOME || "~", "Data", "0-personal", "org", "inbox.org");
|
|
10171
11363
|
if (!existsSync10(inboxPath)) {
|
|
10172
11364
|
throw new CLIError("ERR_NOT_FOUND", "inbox.org not found. Run datacore init first.");
|
|
10173
11365
|
}
|
|
@@ -10191,13 +11383,13 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10191
11383
|
break;
|
|
10192
11384
|
}
|
|
10193
11385
|
case "cron": {
|
|
10194
|
-
const { execSync:
|
|
11386
|
+
const { execSync: execSync5, exec: execAsync } = await import("child_process");
|
|
10195
11387
|
switch (action) {
|
|
10196
11388
|
case "install": {
|
|
10197
11389
|
info("Installing cron jobs...");
|
|
10198
11390
|
let currentCron = "";
|
|
10199
11391
|
try {
|
|
10200
|
-
currentCron =
|
|
11392
|
+
currentCron = execSync5("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
10201
11393
|
} catch {}
|
|
10202
11394
|
if (currentCron.includes("# Datacore")) {
|
|
10203
11395
|
if (!flags.force) {
|
|
@@ -10220,7 +11412,7 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10220
11412
|
`;
|
|
10221
11413
|
const newCron = currentCron.trim() + `
|
|
10222
11414
|
` + datacoreCron;
|
|
10223
|
-
|
|
11415
|
+
execSync5(`echo "${newCron}" | crontab -`, { stdio: "pipe" });
|
|
10224
11416
|
success("Cron jobs installed");
|
|
10225
11417
|
info(" 8:00 AM weekdays - Daily briefing");
|
|
10226
11418
|
info(" 6:00 PM weekdays - Evening wrap-up");
|
|
@@ -10230,7 +11422,7 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10230
11422
|
case "status": {
|
|
10231
11423
|
let crontab = "";
|
|
10232
11424
|
try {
|
|
10233
|
-
crontab =
|
|
11425
|
+
crontab = execSync5("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
10234
11426
|
} catch {}
|
|
10235
11427
|
const datacoreJobs = crontab.split(`
|
|
10236
11428
|
`).filter((line) => line.includes("datacore") && !line.startsWith("#"));
|
|
@@ -10253,7 +11445,7 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10253
11445
|
info("Removing cron jobs...");
|
|
10254
11446
|
let currentCron = "";
|
|
10255
11447
|
try {
|
|
10256
|
-
currentCron =
|
|
11448
|
+
currentCron = execSync5("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
10257
11449
|
} catch {
|
|
10258
11450
|
info("No cron jobs to remove");
|
|
10259
11451
|
break;
|
|
@@ -10262,9 +11454,9 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10262
11454
|
`).filter((line) => !line.includes("# Datacore") && !line.includes("datacore")).join(`
|
|
10263
11455
|
`).trim();
|
|
10264
11456
|
if (newCron) {
|
|
10265
|
-
|
|
11457
|
+
execSync5(`echo "${newCron}" | crontab -`, { stdio: "pipe" });
|
|
10266
11458
|
} else {
|
|
10267
|
-
|
|
11459
|
+
execSync5("crontab -r", { stdio: "pipe" });
|
|
10268
11460
|
}
|
|
10269
11461
|
success("Cron jobs removed");
|
|
10270
11462
|
break;
|