@datacore-one/cli 1.0.6 → 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 +2728 -1622
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6980,727 +6980,1162 @@ var require_dist = __commonJS((exports) => {
|
|
|
6980
6980
|
exports.visitAsync = visit.visitAsync;
|
|
6981
6981
|
});
|
|
6982
6982
|
|
|
6983
|
-
// src/lib/
|
|
6984
|
-
var
|
|
6985
|
-
__export(
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
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
|
|
6989
6991
|
});
|
|
6990
|
-
import {
|
|
6991
|
-
import {
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
const { execSync: execSync6 } = __require("child_process");
|
|
6996
|
-
execSync6(`which ${cmd}`, { stdio: "pipe" });
|
|
6997
|
-
return true;
|
|
6998
|
-
} catch {
|
|
6999
|
-
return false;
|
|
7000
|
-
}
|
|
7001
|
-
}
|
|
7002
|
-
async function invokeAgent(invocation, options = {}) {
|
|
7003
|
-
const { stream = false, cwd = DATA_DIR7, timeout = 300000 } = options;
|
|
7004
|
-
if (!commandExists2("claude")) {
|
|
7005
|
-
return {
|
|
7006
|
-
success: false,
|
|
7007
|
-
output: "",
|
|
7008
|
-
error: "Claude Code CLI not found. Install with: npm install -g @anthropic-ai/claude-code"
|
|
7009
|
-
};
|
|
7010
|
-
}
|
|
7011
|
-
if (!existsSync9(cwd)) {
|
|
7012
|
-
return {
|
|
7013
|
-
success: false,
|
|
7014
|
-
output: "",
|
|
7015
|
-
error: `Working directory not found: ${cwd}`
|
|
7016
|
-
};
|
|
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 [];
|
|
7017
6997
|
}
|
|
7018
|
-
const
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
const
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
const text = data.toString();
|
|
7037
|
-
chunks.push(text);
|
|
7038
|
-
if (stream) {
|
|
7039
|
-
process.stdout.write(text);
|
|
7040
|
-
}
|
|
7041
|
-
});
|
|
7042
|
-
proc.stderr?.on("data", (data) => {
|
|
7043
|
-
errorChunks.push(data.toString());
|
|
7044
|
-
});
|
|
7045
|
-
proc.on("close", (code) => {
|
|
7046
|
-
clearTimeout(timer);
|
|
7047
|
-
const output2 = chunks.join("");
|
|
7048
|
-
const stderr = errorChunks.join("");
|
|
7049
|
-
if (timedOut) {
|
|
7050
|
-
resolve({
|
|
7051
|
-
success: false,
|
|
7052
|
-
output: output2,
|
|
7053
|
-
error: `Agent timed out after ${timeout}ms`
|
|
7054
|
-
});
|
|
7055
|
-
return;
|
|
7056
|
-
}
|
|
7057
|
-
if (code !== 0) {
|
|
7058
|
-
resolve({
|
|
7059
|
-
success: false,
|
|
7060
|
-
output: output2,
|
|
7061
|
-
error: stderr || `Agent exited with code ${code}`
|
|
7062
|
-
});
|
|
7063
|
-
return;
|
|
7064
|
-
}
|
|
7065
|
-
const artifacts = parseArtifacts(output2);
|
|
7066
|
-
resolve({
|
|
7067
|
-
success: true,
|
|
7068
|
-
output: output2,
|
|
7069
|
-
artifacts: Object.keys(artifacts).length > 0 ? artifacts : undefined
|
|
7070
|
-
});
|
|
7071
|
-
});
|
|
7072
|
-
proc.on("error", (err) => {
|
|
7073
|
-
clearTimeout(timer);
|
|
7074
|
-
resolve({
|
|
7075
|
-
success: false,
|
|
7076
|
-
output: chunks.join(""),
|
|
7077
|
-
error: err.message
|
|
7078
|
-
});
|
|
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"))
|
|
7079
7016
|
});
|
|
7080
|
-
});
|
|
7081
|
-
}
|
|
7082
|
-
function buildAgentPrompt(invocation) {
|
|
7083
|
-
const { agent, params } = invocation;
|
|
7084
|
-
const paramLines = Object.entries(params).map(([key, value]) => `- ${key}: ${JSON.stringify(value)}`).join(`
|
|
7085
|
-
`);
|
|
7086
|
-
return `Use the Task tool to invoke the "${agent}" agent with the following parameters:
|
|
7087
|
-
|
|
7088
|
-
${paramLines || "(no parameters)"}
|
|
7089
|
-
|
|
7090
|
-
Wait for the agent to complete and return its results.`;
|
|
7091
|
-
}
|
|
7092
|
-
function parseArtifacts(output2) {
|
|
7093
|
-
const artifacts = {};
|
|
7094
|
-
const regex = /<!-- ARTIFACT:([^:]+):([^ ]+) -->/g;
|
|
7095
|
-
let match;
|
|
7096
|
-
while ((match = regex.exec(output2)) !== null) {
|
|
7097
|
-
const [, name, path] = match;
|
|
7098
|
-
if (name && path) {
|
|
7099
|
-
artifacts[name] = path;
|
|
7100
|
-
}
|
|
7101
7017
|
}
|
|
7102
|
-
|
|
7018
|
+
spaces.sort((a, b) => a.number - b.number);
|
|
7019
|
+
return spaces;
|
|
7103
7020
|
}
|
|
7104
|
-
function
|
|
7105
|
-
const
|
|
7106
|
-
if (
|
|
7107
|
-
return
|
|
7108
|
-
}
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
return registry.agents?.map((a) => a.name) || [];
|
|
7115
|
-
} catch {
|
|
7116
|
-
return [];
|
|
7117
|
-
}
|
|
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;
|
|
7118
7031
|
}
|
|
7119
|
-
function
|
|
7120
|
-
|
|
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;
|
|
7121
7038
|
}
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
}
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
var META_COMMANDS = [
|
|
7130
|
-
"init",
|
|
7131
|
-
"doctor",
|
|
7132
|
-
"ingest",
|
|
7133
|
-
"sync",
|
|
7134
|
-
"today",
|
|
7135
|
-
"tomorrow",
|
|
7136
|
-
"version"
|
|
7137
|
-
];
|
|
7138
|
-
var ACTIONS = {
|
|
7139
|
-
space: ["create", "list"],
|
|
7140
|
-
module: ["install", "list", "update", "remove"],
|
|
7141
|
-
config: ["show", "get", "set"],
|
|
7142
|
-
nightshift: ["status", "trigger", "queue"],
|
|
7143
|
-
cron: ["install", "status", "remove"],
|
|
7144
|
-
snapshot: ["create", "restore", "diff", "show"]
|
|
7145
|
-
};
|
|
7146
|
-
function parseArgs(argv) {
|
|
7147
|
-
if (argv.length === 0) {
|
|
7148
|
-
return { type: "help", topic: undefined, subtopic: undefined };
|
|
7149
|
-
}
|
|
7150
|
-
const [first, ...rest] = argv;
|
|
7151
|
-
if (!first) {
|
|
7152
|
-
return { type: "help", topic: undefined, subtopic: undefined };
|
|
7153
|
-
}
|
|
7154
|
-
const helpIndex = argv.findIndex((a) => a === "--help" || a === "-h");
|
|
7155
|
-
if (helpIndex !== -1) {
|
|
7156
|
-
if (helpIndex === 0) {
|
|
7157
|
-
return { type: "help", topic: undefined, subtopic: undefined };
|
|
7158
|
-
}
|
|
7159
|
-
if (helpIndex === 1 && RESOURCES.includes(first)) {
|
|
7160
|
-
return { type: "help", topic: first, subtopic: undefined };
|
|
7161
|
-
}
|
|
7162
|
-
if (helpIndex === 2 && RESOURCES.includes(first)) {
|
|
7163
|
-
return { type: "help", topic: first, subtopic: rest[0] };
|
|
7164
|
-
}
|
|
7165
|
-
return { type: "help", topic: first, subtopic: undefined };
|
|
7166
|
-
}
|
|
7167
|
-
if (first === "--version" || first === "-v") {
|
|
7168
|
-
return { type: "meta", command: "version", args: [], flags: {} };
|
|
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}`);
|
|
7169
7046
|
}
|
|
7170
|
-
|
|
7171
|
-
|
|
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 });
|
|
7172
7095
|
}
|
|
7173
|
-
if (
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
}
|
|
7177
|
-
if (RESOURCES.includes(first)) {
|
|
7178
|
-
const action = rest[0] || "list";
|
|
7179
|
-
const { args, flags } = parseRest(rest.slice(1));
|
|
7180
|
-
return { type: "resource", resource: first, action, args, flags };
|
|
7181
|
-
}
|
|
7182
|
-
return { type: "unknown", command: first };
|
|
7183
|
-
}
|
|
7184
|
-
function parseRest(argv) {
|
|
7185
|
-
const args = [];
|
|
7186
|
-
const flags = {};
|
|
7187
|
-
for (let i = 0;i < argv.length; i++) {
|
|
7188
|
-
const arg = argv[i];
|
|
7189
|
-
if (!arg)
|
|
7190
|
-
continue;
|
|
7191
|
-
if (arg.startsWith("--")) {
|
|
7192
|
-
if (arg.includes("=")) {
|
|
7193
|
-
const [key, value] = arg.slice(2).split("=");
|
|
7194
|
-
if (key)
|
|
7195
|
-
flags[key] = value ?? true;
|
|
7196
|
-
} else {
|
|
7197
|
-
const key = arg.slice(2);
|
|
7198
|
-
const next = argv[i + 1];
|
|
7199
|
-
if (next && !next.startsWith("-")) {
|
|
7200
|
-
flags[key] = next;
|
|
7201
|
-
i++;
|
|
7202
|
-
} else {
|
|
7203
|
-
flags[key] = true;
|
|
7204
|
-
}
|
|
7205
|
-
}
|
|
7206
|
-
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
7207
|
-
const key = arg.slice(1);
|
|
7208
|
-
flags[key] = true;
|
|
7209
|
-
} else {
|
|
7210
|
-
args.push(arg);
|
|
7211
|
-
}
|
|
7212
|
-
}
|
|
7213
|
-
return { args, flags };
|
|
7214
|
-
}
|
|
7215
|
-
function suggestCommand(unknown) {
|
|
7216
|
-
const allCommands = [...META_COMMANDS, ...RESOURCES];
|
|
7217
|
-
for (const cmd of allCommands) {
|
|
7218
|
-
if (cmd.startsWith(unknown) || unknown.startsWith(cmd)) {
|
|
7219
|
-
return cmd;
|
|
7220
|
-
}
|
|
7221
|
-
if (Math.abs(cmd.length - unknown.length) <= 1) {
|
|
7222
|
-
let diff = 0;
|
|
7223
|
-
for (let i = 0;i < Math.max(cmd.length, unknown.length); i++) {
|
|
7224
|
-
if (cmd[i] !== unknown[i])
|
|
7225
|
-
diff++;
|
|
7226
|
-
}
|
|
7227
|
-
if (diff <= 2)
|
|
7228
|
-
return cmd;
|
|
7229
|
-
}
|
|
7230
|
-
}
|
|
7231
|
-
return null;
|
|
7232
|
-
}
|
|
7233
|
-
|
|
7234
|
-
// src/help.ts
|
|
7235
|
-
function showHelp() {
|
|
7236
|
-
console.log(`datacore - AI Second Brain Setup & Management CLI
|
|
7096
|
+
if (type === "personal") {
|
|
7097
|
+
writeFileSync2(join3(spacePath, "org", "inbox.org"), `#+TITLE: Inbox
|
|
7098
|
+
#+FILETAGS: :inbox:
|
|
7237
7099
|
|
|
7238
|
-
|
|
7100
|
+
Capture everything here. Process daily to zero.
|
|
7239
7101
|
|
|
7240
|
-
|
|
7241
|
-
|
|
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:
|
|
7242
7108
|
|
|
7243
|
-
|
|
7244
|
-
init Set up a new Datacore installation
|
|
7245
|
-
doctor Check dependencies and system status
|
|
7246
|
-
ingest <path> Import files during setup
|
|
7109
|
+
Tasks organized by focus area. Tag with :AI: to delegate to agents.
|
|
7247
7110
|
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
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:
|
|
7253
7129
|
|
|
7254
|
-
|
|
7255
|
-
sync Sync all git repos
|
|
7256
|
-
today Generate daily briefing
|
|
7257
|
-
tomorrow End-of-day wrap-up
|
|
7258
|
-
nightshift Queue and trigger AI tasks
|
|
7259
|
-
cron Set up scheduled tasks
|
|
7130
|
+
AI tasks queued for overnight execution. Managed by /tomorrow command.
|
|
7260
7131
|
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7132
|
+
* Queue
|
|
7133
|
+
`);
|
|
7134
|
+
writeFileSync2(join3(spacePath, "org", "habits.org"), `#+TITLE: Habits
|
|
7135
|
+
#+FILETAGS: :habits:
|
|
7264
7136
|
|
|
7265
|
-
|
|
7266
|
-
--format json|human Output format (auto-detects TTY)
|
|
7267
|
-
--help, -h Show help for any command
|
|
7268
|
-
--yes, -y Skip confirmation prompts
|
|
7137
|
+
Recurring behaviors and routines. Track with org-habit.
|
|
7269
7138
|
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7139
|
+
* Daily
|
|
7140
|
+
* Weekly
|
|
7141
|
+
* Monthly
|
|
7142
|
+
`);
|
|
7143
|
+
writeFileSync2(join3(spacePath, "org", "someday.org"), `#+TITLE: Someday/Maybe
|
|
7144
|
+
#+FILETAGS: :someday:
|
|
7274
7145
|
|
|
7275
|
-
|
|
7276
|
-
datacore snapshot create
|
|
7277
|
-
datacore module install nightshift
|
|
7146
|
+
Ideas and projects for the future. Review monthly.
|
|
7278
7147
|
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
showInitHelp();
|
|
7285
|
-
return;
|
|
7286
|
-
}
|
|
7287
|
-
if (resource === "doctor") {
|
|
7288
|
-
showDoctorHelp();
|
|
7289
|
-
return;
|
|
7290
|
-
}
|
|
7291
|
-
if (resource === "ingest") {
|
|
7292
|
-
showIngestHelp();
|
|
7293
|
-
return;
|
|
7294
|
-
}
|
|
7295
|
-
if (resource === "sync") {
|
|
7296
|
-
showSyncHelp();
|
|
7297
|
-
return;
|
|
7298
|
-
}
|
|
7299
|
-
if (resource === "today" || resource === "tomorrow") {
|
|
7300
|
-
showGtdMetaHelp(resource);
|
|
7301
|
-
return;
|
|
7302
|
-
}
|
|
7303
|
-
if (!RESOURCES.includes(resource)) {
|
|
7304
|
-
console.log(`Unknown command: ${resource}
|
|
7148
|
+
* Someday
|
|
7149
|
+
* Maybe
|
|
7150
|
+
`);
|
|
7151
|
+
writeFileSync2(join3(spacePath, "org", "archive.org"), `#+TITLE: Archive
|
|
7152
|
+
#+FILETAGS: :archive:
|
|
7305
7153
|
|
|
7306
|
-
|
|
7154
|
+
Completed and canceled tasks. Searchable history.
|
|
7307
7155
|
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
}
|
|
7311
|
-
|
|
7312
|
-
|
|
7156
|
+
* Archived Tasks
|
|
7157
|
+
`);
|
|
7158
|
+
} else {
|
|
7159
|
+
writeFileSync2(join3(spacePath, "org", "inbox.org"), `#+TITLE: Inbox
|
|
7160
|
+
#+FILETAGS: :inbox:
|
|
7313
7161
|
|
|
7314
|
-
|
|
7315
|
-
|
|
7162
|
+
* Capture items here
|
|
7163
|
+
`);
|
|
7164
|
+
writeFileSync2(join3(spacePath, "org", "next_actions.org"), `#+TITLE: Next Actions
|
|
7165
|
+
#+FILETAGS: :tasks:
|
|
7316
7166
|
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7167
|
+
* Tasks
|
|
7168
|
+
** TODO items go here
|
|
7169
|
+
`);
|
|
7320
7170
|
}
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
datacore ${resource} <action> --help
|
|
7324
|
-
datacore help ${resource} <action>`);
|
|
7325
|
-
}
|
|
7326
|
-
function showInitHelp() {
|
|
7327
|
-
console.log(`datacore init - Set up a new Datacore installation
|
|
7171
|
+
if (type === "personal") {
|
|
7172
|
+
writeFileSync2(join3(spacePath, "_index.md"), `# Personal Space
|
|
7328
7173
|
|
|
7329
|
-
|
|
7330
|
-
datacore init [options]
|
|
7174
|
+
Your personal knowledge base and GTD system.
|
|
7331
7175
|
|
|
7332
|
-
|
|
7333
|
-
Interactive wizard that sets up your Datacore second brain:
|
|
7334
|
-
1. Creates ~/Data directory structure
|
|
7335
|
-
2. Initializes git repositories
|
|
7336
|
-
3. Generates CLAUDE.md context files
|
|
7337
|
-
4. Creates personal space (0-personal)
|
|
7338
|
-
5. Optionally installs recommended modules
|
|
7176
|
+
## GTD Workflow
|
|
7339
7177
|
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
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
|
|
7344
7183
|
|
|
7345
|
-
|
|
7346
|
-
# Interactive setup
|
|
7347
|
-
datacore init
|
|
7184
|
+
## Org Files (GTD)
|
|
7348
7185
|
|
|
7349
|
-
|
|
7350
|
-
|
|
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 |
|
|
7351
7194
|
|
|
7352
|
-
|
|
7353
|
-
datacore init --path ~/my-data`);
|
|
7354
|
-
}
|
|
7355
|
-
function showDoctorHelp() {
|
|
7356
|
-
console.log(`datacore doctor - Check dependencies and system status
|
|
7195
|
+
## Knowledge
|
|
7357
7196
|
|
|
7358
|
-
|
|
7359
|
-
|
|
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) |
|
|
7360
7202
|
|
|
7361
|
-
|
|
7362
|
-
Verifies all required dependencies are installed and checks
|
|
7363
|
-
the health of your Datacore installation.
|
|
7203
|
+
## Other
|
|
7364
7204
|
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
Recommended:
|
|
7373
|
-
- claude Claude Code CLI for AI features
|
|
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}
|
|
7374
7212
|
|
|
7375
|
-
|
|
7376
|
-
- ~/Data exists
|
|
7377
|
-
- .datacore/ configured
|
|
7378
|
-
- Spaces detected
|
|
7213
|
+
Team space.
|
|
7379
7214
|
|
|
7380
|
-
|
|
7381
|
-
--format json Output as JSON
|
|
7215
|
+
## Structure
|
|
7382
7216
|
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
console.log(`datacore ingest - Import files with AI processing
|
|
7217
|
+
- \`org/\` - Task coordination
|
|
7218
|
+
- \`1-tracks/\` - Department work
|
|
7219
|
+
- \`2-projects/\` - Code repositories
|
|
7220
|
+
- \`3-knowledge/\` - Shared knowledge
|
|
7221
|
+
- \`4-archive/\` - Historical content
|
|
7389
7222
|
|
|
7390
|
-
|
|
7391
|
-
datacore ingest <path> [options]
|
|
7223
|
+
## Quick Links
|
|
7392
7224
|
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
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
|
|
7397
7233
|
|
|
7398
|
-
|
|
7399
|
-
2. ASSESS - Determine destination
|
|
7400
|
-
3. EXTRACT - Pull out knowledge (zettels, insights)
|
|
7401
|
-
4. CAPTURE - Log tasks and TODOs
|
|
7402
|
-
5. FILE - Move to semantic location
|
|
7403
|
-
6. LINK - Connect to knowledge graph
|
|
7234
|
+
Personal GTD system and knowledge base (per DIP-0009).
|
|
7404
7235
|
|
|
7405
|
-
|
|
7406
|
-
--space <name> Target space (default: 0-personal)
|
|
7407
|
-
--dry-run Show what would happen without doing it
|
|
7236
|
+
## GTD Files
|
|
7408
7237
|
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
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 |
|
|
7412
7246
|
|
|
7413
|
-
|
|
7414
|
-
datacore ingest ~/Documents/export/
|
|
7247
|
+
## Task States
|
|
7415
7248
|
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
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) |
|
|
7421
7256
|
|
|
7422
|
-
|
|
7423
|
-
datacore sync [action] [options]
|
|
7257
|
+
## AI Delegation
|
|
7424
7258
|
|
|
7425
|
-
|
|
7426
|
-
(none) Pull all repos (default)
|
|
7427
|
-
push Commit and push changes
|
|
7428
|
-
status Show git status overview
|
|
7259
|
+
Tag tasks with \`:AI:\` to delegate to agents:
|
|
7429
7260
|
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
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 |
|
|
7433
7267
|
|
|
7434
|
-
|
|
7435
|
-
--message <msg> Commit message (for push)
|
|
7268
|
+
## Knowledge Structure
|
|
7436
7269
|
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
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 |
|
|
7440
7276
|
|
|
7441
|
-
|
|
7442
|
-
datacore sync push --message "Daily update"
|
|
7277
|
+
## Daily Workflow
|
|
7443
7278
|
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
console.log(`datacore today - Generate daily briefing
|
|
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
|
|
7450
7284
|
|
|
7451
|
-
|
|
7452
|
-
datacore today [options]
|
|
7285
|
+
## See Also
|
|
7453
7286
|
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
- Priority tasks
|
|
7459
|
-
- Nightshift results (completed AI tasks)
|
|
7460
|
-
- Journal entry
|
|
7287
|
+
Parent: ~/Data/CLAUDE.md
|
|
7288
|
+
`);
|
|
7289
|
+
} else {
|
|
7290
|
+
writeFileSync2(join3(spacePath, "CLAUDE.base.md"), `# ${name} Space
|
|
7461
7291
|
|
|
7462
|
-
|
|
7463
|
-
--format json Output as JSON
|
|
7292
|
+
Team space for ${name}.
|
|
7464
7293
|
|
|
7465
|
-
|
|
7466
|
-
datacore today`);
|
|
7467
|
-
} else {
|
|
7468
|
-
console.log(`datacore tomorrow - End-of-day wrap-up
|
|
7294
|
+
## Structure
|
|
7469
7295
|
|
|
7470
|
-
|
|
7471
|
-
|
|
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
|
|
7472
7304
|
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
- Session summary
|
|
7477
|
-
- Queuing AI tasks for overnight
|
|
7478
|
-
- Learning extraction
|
|
7479
|
-
- Journal update
|
|
7305
|
+
Successful approaches to remember.
|
|
7306
|
+
`);
|
|
7307
|
+
writeFileSync2(join3(spacePath, ".datacore", "learning", "corrections.md"), `# Corrections
|
|
7480
7308
|
|
|
7481
|
-
|
|
7482
|
-
|
|
7309
|
+
Human feedback log.
|
|
7310
|
+
`);
|
|
7311
|
+
writeFileSync2(join3(spacePath, ".datacore", "learning", "preferences.md"), `# Preferences
|
|
7483
7312
|
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
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
|
+
};
|
|
7487
7333
|
}
|
|
7488
|
-
function
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
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;
|
|
7492
7347
|
}
|
|
7493
|
-
const
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
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
|
+
}
|
|
7499
7363
|
}
|
|
7500
|
-
const
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
const
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
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
|
|
7511
7411
|
};
|
|
7512
|
-
return descriptions[resource];
|
|
7513
7412
|
}
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7413
|
+
var DATA_DIR2;
|
|
7414
|
+
var init_space = __esm(() => {
|
|
7415
|
+
DATA_DIR2 = join3(process.env.HOME || "~", "Data");
|
|
7416
|
+
});
|
|
7417
|
+
|
|
7418
|
+
// src/lib/agent.ts
|
|
7419
|
+
var exports_agent = {};
|
|
7420
|
+
__export(exports_agent, {
|
|
7421
|
+
listAgents: () => listAgents,
|
|
7422
|
+
invokeAgent: () => invokeAgent,
|
|
7423
|
+
agentExists: () => agentExists
|
|
7424
|
+
});
|
|
7425
|
+
import { spawn as spawn2 } from "child_process";
|
|
7426
|
+
import { existsSync as existsSync6 } from "fs";
|
|
7427
|
+
import { join as join6 } from "path";
|
|
7428
|
+
function commandExists2(cmd) {
|
|
7429
|
+
try {
|
|
7430
|
+
const { execSync: execSync4 } = __require("child_process");
|
|
7431
|
+
execSync4(`which ${cmd}`, { stdio: "pipe" });
|
|
7432
|
+
return true;
|
|
7433
|
+
} catch {
|
|
7434
|
+
return false;
|
|
7435
|
+
}
|
|
7537
7436
|
}
|
|
7538
|
-
function
|
|
7539
|
-
const
|
|
7540
|
-
|
|
7541
|
-
|
|
7437
|
+
async function invokeAgent(invocation, options = {}) {
|
|
7438
|
+
const { stream = false, cwd = DATA_DIR5, timeout = 300000 } = options;
|
|
7439
|
+
if (!commandExists2("claude")) {
|
|
7440
|
+
return {
|
|
7441
|
+
success: false,
|
|
7442
|
+
output: "",
|
|
7443
|
+
error: "Claude Code CLI not found. Install with: npm install -g @anthropic-ai/claude-code"
|
|
7444
|
+
};
|
|
7445
|
+
}
|
|
7446
|
+
if (!existsSync6(cwd)) {
|
|
7447
|
+
return {
|
|
7448
|
+
success: false,
|
|
7449
|
+
output: "",
|
|
7450
|
+
error: `Working directory not found: ${cwd}`
|
|
7451
|
+
};
|
|
7452
|
+
}
|
|
7453
|
+
const prompt = buildAgentPrompt(invocation);
|
|
7454
|
+
return new Promise((resolve) => {
|
|
7455
|
+
const chunks = [];
|
|
7456
|
+
let errorChunks = [];
|
|
7457
|
+
let timedOut = false;
|
|
7458
|
+
const proc = spawn2("claude", ["--print", prompt], {
|
|
7459
|
+
cwd,
|
|
7460
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
7461
|
+
env: {
|
|
7462
|
+
...process.env,
|
|
7463
|
+
CI: "true"
|
|
7464
|
+
}
|
|
7465
|
+
});
|
|
7466
|
+
const timer = setTimeout(() => {
|
|
7467
|
+
timedOut = true;
|
|
7468
|
+
proc.kill("SIGTERM");
|
|
7469
|
+
}, timeout);
|
|
7470
|
+
proc.stdout?.on("data", (data) => {
|
|
7471
|
+
const text = data.toString();
|
|
7472
|
+
chunks.push(text);
|
|
7473
|
+
if (stream) {
|
|
7474
|
+
process.stdout.write(text);
|
|
7475
|
+
}
|
|
7476
|
+
});
|
|
7477
|
+
proc.stderr?.on("data", (data) => {
|
|
7478
|
+
errorChunks.push(data.toString());
|
|
7479
|
+
});
|
|
7480
|
+
proc.on("close", (code) => {
|
|
7481
|
+
clearTimeout(timer);
|
|
7482
|
+
const output2 = chunks.join("");
|
|
7483
|
+
const stderr = errorChunks.join("");
|
|
7484
|
+
if (timedOut) {
|
|
7485
|
+
resolve({
|
|
7486
|
+
success: false,
|
|
7487
|
+
output: output2,
|
|
7488
|
+
error: `Agent timed out after ${timeout}ms`
|
|
7489
|
+
});
|
|
7490
|
+
return;
|
|
7491
|
+
}
|
|
7492
|
+
if (code !== 0) {
|
|
7493
|
+
resolve({
|
|
7494
|
+
success: false,
|
|
7495
|
+
output: output2,
|
|
7496
|
+
error: stderr || `Agent exited with code ${code}`
|
|
7497
|
+
});
|
|
7498
|
+
return;
|
|
7499
|
+
}
|
|
7500
|
+
const artifacts = parseArtifacts(output2);
|
|
7501
|
+
resolve({
|
|
7502
|
+
success: true,
|
|
7503
|
+
output: output2,
|
|
7504
|
+
artifacts: Object.keys(artifacts).length > 0 ? artifacts : undefined
|
|
7505
|
+
});
|
|
7506
|
+
});
|
|
7507
|
+
proc.on("error", (err) => {
|
|
7508
|
+
clearTimeout(timer);
|
|
7509
|
+
resolve({
|
|
7510
|
+
success: false,
|
|
7511
|
+
output: chunks.join(""),
|
|
7512
|
+
error: err.message
|
|
7513
|
+
});
|
|
7514
|
+
});
|
|
7515
|
+
});
|
|
7516
|
+
}
|
|
7517
|
+
function buildAgentPrompt(invocation) {
|
|
7518
|
+
const { agent, params } = invocation;
|
|
7519
|
+
const paramLines = Object.entries(params).map(([key, value]) => `- ${key}: ${JSON.stringify(value)}`).join(`
|
|
7520
|
+
`);
|
|
7521
|
+
return `Use the Task tool to invoke the "${agent}" agent with the following parameters:
|
|
7542
7522
|
|
|
7543
|
-
|
|
7544
|
-
datacore space create [options]
|
|
7523
|
+
${paramLines || "(no parameters)"}
|
|
7545
7524
|
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7525
|
+
Wait for the agent to complete and return its results.`;
|
|
7526
|
+
}
|
|
7527
|
+
function parseArtifacts(output2) {
|
|
7528
|
+
const artifacts = {};
|
|
7529
|
+
const regex = /<!-- ARTIFACT:([^:]+):([^ ]+) -->/g;
|
|
7530
|
+
let match;
|
|
7531
|
+
while ((match = regex.exec(output2)) !== null) {
|
|
7532
|
+
const [, name, path] = match;
|
|
7533
|
+
if (name && path) {
|
|
7534
|
+
artifacts[name] = path;
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
return artifacts;
|
|
7538
|
+
}
|
|
7539
|
+
function listAgents() {
|
|
7540
|
+
const registryPath = join6(DATA_DIR5, ".datacore", "registry", "agents.yaml");
|
|
7541
|
+
if (!existsSync6(registryPath)) {
|
|
7542
|
+
return [];
|
|
7543
|
+
}
|
|
7544
|
+
try {
|
|
7545
|
+
const { readFileSync: readFileSync3 } = __require("fs");
|
|
7546
|
+
const { parse: parse2 } = require_dist();
|
|
7547
|
+
const content = readFileSync3(registryPath, "utf-8");
|
|
7548
|
+
const registry = parse2(content);
|
|
7549
|
+
return registry.agents?.map((a) => a.name) || [];
|
|
7550
|
+
} catch {
|
|
7551
|
+
return [];
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
function agentExists(name) {
|
|
7555
|
+
return listAgents().includes(name);
|
|
7556
|
+
}
|
|
7557
|
+
var DATA_DIR5;
|
|
7558
|
+
var init_agent = __esm(() => {
|
|
7559
|
+
DATA_DIR5 = join6(process.env.HOME || "~", "Data");
|
|
7560
|
+
});
|
|
7549
7561
|
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7562
|
+
// src/routing.ts
|
|
7563
|
+
var RESOURCES = ["space", "module", "config", "nightshift", "cron", "snapshot"];
|
|
7564
|
+
var META_COMMANDS = [
|
|
7565
|
+
"init",
|
|
7566
|
+
"doctor",
|
|
7567
|
+
"ingest",
|
|
7568
|
+
"sync",
|
|
7569
|
+
"today",
|
|
7570
|
+
"tomorrow",
|
|
7571
|
+
"version"
|
|
7572
|
+
];
|
|
7573
|
+
var ACTIONS = {
|
|
7574
|
+
space: ["create", "list"],
|
|
7575
|
+
module: ["install", "list", "update", "remove"],
|
|
7576
|
+
config: ["show", "get", "set"],
|
|
7577
|
+
nightshift: ["status", "trigger", "queue"],
|
|
7578
|
+
cron: ["install", "status", "remove"],
|
|
7579
|
+
snapshot: ["create", "restore", "diff", "show"]
|
|
7580
|
+
};
|
|
7581
|
+
function parseArgs(argv) {
|
|
7582
|
+
if (argv.length === 0) {
|
|
7583
|
+
return { type: "help", topic: undefined, subtopic: undefined };
|
|
7584
|
+
}
|
|
7585
|
+
const [first, ...rest] = argv;
|
|
7586
|
+
if (!first) {
|
|
7587
|
+
return { type: "help", topic: undefined, subtopic: undefined };
|
|
7588
|
+
}
|
|
7589
|
+
const helpIndex = argv.findIndex((a) => a === "--help" || a === "-h");
|
|
7590
|
+
if (helpIndex !== -1) {
|
|
7591
|
+
if (helpIndex === 0) {
|
|
7592
|
+
return { type: "help", topic: undefined, subtopic: undefined };
|
|
7593
|
+
}
|
|
7594
|
+
if (helpIndex === 1 && RESOURCES.includes(first)) {
|
|
7595
|
+
return { type: "help", topic: first, subtopic: undefined };
|
|
7596
|
+
}
|
|
7597
|
+
if (helpIndex === 2 && RESOURCES.includes(first)) {
|
|
7598
|
+
return { type: "help", topic: first, subtopic: rest[0] };
|
|
7599
|
+
}
|
|
7600
|
+
return { type: "help", topic: first, subtopic: undefined };
|
|
7601
|
+
}
|
|
7602
|
+
if (first === "--version" || first === "-v") {
|
|
7603
|
+
return { type: "meta", command: "version", args: [], flags: {} };
|
|
7604
|
+
}
|
|
7605
|
+
if (first === "help") {
|
|
7606
|
+
return { type: "help", topic: rest[0], subtopic: rest[1] };
|
|
7607
|
+
}
|
|
7608
|
+
if (META_COMMANDS.includes(first)) {
|
|
7609
|
+
const { args, flags } = parseRest(rest);
|
|
7610
|
+
return { type: "meta", command: first, args, flags };
|
|
7611
|
+
}
|
|
7612
|
+
if (RESOURCES.includes(first)) {
|
|
7613
|
+
const action = rest[0] || "list";
|
|
7614
|
+
const { args, flags } = parseRest(rest.slice(1));
|
|
7615
|
+
return { type: "resource", resource: first, action, args, flags };
|
|
7616
|
+
}
|
|
7617
|
+
return { type: "unknown", command: first };
|
|
7618
|
+
}
|
|
7619
|
+
function parseRest(argv) {
|
|
7620
|
+
const args = [];
|
|
7621
|
+
const flags = {};
|
|
7622
|
+
for (let i = 0;i < argv.length; i++) {
|
|
7623
|
+
const arg = argv[i];
|
|
7624
|
+
if (!arg)
|
|
7625
|
+
continue;
|
|
7626
|
+
if (arg.startsWith("--")) {
|
|
7627
|
+
if (arg.includes("=")) {
|
|
7628
|
+
const [key, value] = arg.slice(2).split("=");
|
|
7629
|
+
if (key)
|
|
7630
|
+
flags[key] = value ?? true;
|
|
7631
|
+
} else {
|
|
7632
|
+
const key = arg.slice(2);
|
|
7633
|
+
const next = argv[i + 1];
|
|
7634
|
+
if (next && !next.startsWith("-")) {
|
|
7635
|
+
flags[key] = next;
|
|
7636
|
+
i++;
|
|
7637
|
+
} else {
|
|
7638
|
+
flags[key] = true;
|
|
7639
|
+
}
|
|
7640
|
+
}
|
|
7641
|
+
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
7642
|
+
const key = arg.slice(1);
|
|
7643
|
+
flags[key] = true;
|
|
7644
|
+
} else {
|
|
7645
|
+
args.push(arg);
|
|
7646
|
+
}
|
|
7647
|
+
}
|
|
7648
|
+
return { args, flags };
|
|
7649
|
+
}
|
|
7650
|
+
function suggestCommand(unknown) {
|
|
7651
|
+
const allCommands = [...META_COMMANDS, ...RESOURCES];
|
|
7652
|
+
for (const cmd of allCommands) {
|
|
7653
|
+
if (cmd.startsWith(unknown) || unknown.startsWith(cmd)) {
|
|
7654
|
+
return cmd;
|
|
7655
|
+
}
|
|
7656
|
+
if (Math.abs(cmd.length - unknown.length) <= 1) {
|
|
7657
|
+
let diff = 0;
|
|
7658
|
+
for (let i = 0;i < Math.max(cmd.length, unknown.length); i++) {
|
|
7659
|
+
if (cmd[i] !== unknown[i])
|
|
7660
|
+
diff++;
|
|
7661
|
+
}
|
|
7662
|
+
if (diff <= 2)
|
|
7663
|
+
return cmd;
|
|
7664
|
+
}
|
|
7665
|
+
}
|
|
7666
|
+
return null;
|
|
7667
|
+
}
|
|
7554
7668
|
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
datacore
|
|
7558
|
-
"space list": `datacore space list - List all spaces
|
|
7669
|
+
// src/help.ts
|
|
7670
|
+
function showHelp() {
|
|
7671
|
+
console.log(`datacore - AI Second Brain Setup & Management CLI
|
|
7559
7672
|
|
|
7560
|
-
|
|
7561
|
-
datacore space list [options]
|
|
7673
|
+
Setup and admin tool for Datacore. For daily use, run: cd ~/Data && claude
|
|
7562
7674
|
|
|
7563
|
-
|
|
7564
|
-
|
|
7675
|
+
Usage:
|
|
7676
|
+
datacore <command> [args] [options]
|
|
7565
7677
|
|
|
7566
|
-
|
|
7567
|
-
|
|
7678
|
+
Setup:
|
|
7679
|
+
init Set up a new Datacore installation
|
|
7680
|
+
doctor Check dependencies and system status
|
|
7681
|
+
ingest <path> Import files during setup
|
|
7568
7682
|
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7683
|
+
Admin:
|
|
7684
|
+
space Create and list spaces
|
|
7685
|
+
module Install and manage modules
|
|
7686
|
+
snapshot Create/restore installation snapshots
|
|
7687
|
+
config View and modify settings
|
|
7572
7688
|
|
|
7573
|
-
|
|
7574
|
-
|
|
7689
|
+
Automation (for cron jobs):
|
|
7690
|
+
sync Sync all git repos
|
|
7691
|
+
today Generate daily briefing
|
|
7692
|
+
tomorrow End-of-day wrap-up
|
|
7693
|
+
nightshift Queue and trigger AI tasks
|
|
7694
|
+
cron Set up scheduled tasks
|
|
7575
7695
|
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7696
|
+
Meta:
|
|
7697
|
+
version Show version
|
|
7698
|
+
help [topic] Show help for a topic
|
|
7579
7699
|
|
|
7580
|
-
|
|
7581
|
-
|
|
7700
|
+
Options:
|
|
7701
|
+
--format json|human Output format (auto-detects TTY)
|
|
7702
|
+
--help, -h Show help for any command
|
|
7703
|
+
--yes, -y Skip confirmation prompts
|
|
7582
7704
|
|
|
7583
7705
|
Examples:
|
|
7706
|
+
# Initial setup
|
|
7707
|
+
datacore init
|
|
7708
|
+
datacore ingest ~/Documents/chatgpt-export/
|
|
7709
|
+
|
|
7710
|
+
# Admin
|
|
7711
|
+
datacore snapshot create
|
|
7584
7712
|
datacore module install nightshift
|
|
7585
|
-
datacore module install crm`,
|
|
7586
|
-
"module list": `datacore module list - List modules
|
|
7587
7713
|
|
|
7588
|
-
|
|
7589
|
-
|
|
7714
|
+
# Then use Claude Code for daily workflow
|
|
7715
|
+
cd ~/Data && claude`);
|
|
7716
|
+
}
|
|
7717
|
+
function showResourceHelp(resource) {
|
|
7718
|
+
if (resource === "init") {
|
|
7719
|
+
showInitHelp();
|
|
7720
|
+
return;
|
|
7721
|
+
}
|
|
7722
|
+
if (resource === "doctor") {
|
|
7723
|
+
showDoctorHelp();
|
|
7724
|
+
return;
|
|
7725
|
+
}
|
|
7726
|
+
if (resource === "ingest") {
|
|
7727
|
+
showIngestHelp();
|
|
7728
|
+
return;
|
|
7729
|
+
}
|
|
7730
|
+
if (resource === "sync") {
|
|
7731
|
+
showSyncHelp();
|
|
7732
|
+
return;
|
|
7733
|
+
}
|
|
7734
|
+
if (resource === "today" || resource === "tomorrow") {
|
|
7735
|
+
showGtdMetaHelp(resource);
|
|
7736
|
+
return;
|
|
7737
|
+
}
|
|
7738
|
+
if (!RESOURCES.includes(resource)) {
|
|
7739
|
+
console.log(`Unknown command: ${resource}
|
|
7590
7740
|
|
|
7591
|
-
|
|
7592
|
-
Shows installed modules and available modules from catalog.
|
|
7741
|
+
Available commands: init, doctor, ingest, sync, today, tomorrow, space, module, config, nightshift, cron, snapshot
|
|
7593
7742
|
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7743
|
+
Run 'datacore help' for overview.`);
|
|
7744
|
+
return;
|
|
7745
|
+
}
|
|
7746
|
+
const actions = ACTIONS[resource];
|
|
7747
|
+
console.log(`datacore ${resource} - ${getResourceDescription(resource)}
|
|
7597
7748
|
|
|
7598
|
-
|
|
7599
|
-
datacore
|
|
7600
|
-
|
|
7601
|
-
|
|
7749
|
+
Usage:
|
|
7750
|
+
datacore ${resource} <action> [args] [options]
|
|
7751
|
+
|
|
7752
|
+
Actions:`);
|
|
7753
|
+
for (const action of actions) {
|
|
7754
|
+
console.log(` ${action.padEnd(18)} ${getActionDescription(resource, action)}`);
|
|
7755
|
+
}
|
|
7756
|
+
console.log(`
|
|
7757
|
+
For detailed help on an action:
|
|
7758
|
+
datacore ${resource} <action> --help
|
|
7759
|
+
datacore help ${resource} <action>`);
|
|
7760
|
+
}
|
|
7761
|
+
function showInitHelp() {
|
|
7762
|
+
console.log(`datacore init - Set up a new Datacore installation
|
|
7602
7763
|
|
|
7603
7764
|
Usage:
|
|
7604
|
-
datacore
|
|
7765
|
+
datacore init [options]
|
|
7605
7766
|
|
|
7606
7767
|
Description:
|
|
7607
|
-
|
|
7768
|
+
Interactive wizard that sets up your Datacore second brain:
|
|
7769
|
+
1. Creates ~/Data directory structure
|
|
7770
|
+
2. Initializes git repositories
|
|
7771
|
+
3. Generates CLAUDE.md context files
|
|
7772
|
+
4. Creates personal space (0-personal)
|
|
7773
|
+
5. Optionally installs recommended modules
|
|
7608
7774
|
|
|
7609
7775
|
Options:
|
|
7610
|
-
--
|
|
7776
|
+
--path <dir> Installation directory (default: ~/Data)
|
|
7777
|
+
--yes, -y Use defaults, skip prompts
|
|
7778
|
+
--no-claude Skip Claude Code check
|
|
7611
7779
|
|
|
7612
7780
|
Examples:
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
Usage:
|
|
7617
|
-
datacore config get <key>
|
|
7781
|
+
# Interactive setup
|
|
7782
|
+
datacore init
|
|
7618
7783
|
|
|
7619
|
-
|
|
7620
|
-
|
|
7784
|
+
# Non-interactive with defaults
|
|
7785
|
+
datacore init --yes
|
|
7621
7786
|
|
|
7622
|
-
|
|
7623
|
-
datacore
|
|
7624
|
-
|
|
7625
|
-
|
|
7787
|
+
# Custom location
|
|
7788
|
+
datacore init --path ~/my-data`);
|
|
7789
|
+
}
|
|
7790
|
+
function showDoctorHelp() {
|
|
7791
|
+
console.log(`datacore doctor - Check dependencies and system status
|
|
7626
7792
|
|
|
7627
7793
|
Usage:
|
|
7628
|
-
datacore
|
|
7794
|
+
datacore doctor [options]
|
|
7629
7795
|
|
|
7630
7796
|
Description:
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
Arguments:
|
|
7634
|
-
<key> Setting key
|
|
7635
|
-
<value> New value
|
|
7797
|
+
Verifies all required dependencies are installed and checks
|
|
7798
|
+
the health of your Datacore installation.
|
|
7636
7799
|
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7800
|
+
Checks:
|
|
7801
|
+
Required:
|
|
7802
|
+
- git Version control
|
|
7803
|
+
- git-lfs Large file support
|
|
7804
|
+
- node >= 18 Node.js runtime
|
|
7805
|
+
- python >= 3.9 Python runtime
|
|
7641
7806
|
|
|
7642
|
-
|
|
7643
|
-
|
|
7807
|
+
Recommended:
|
|
7808
|
+
- claude Claude Code CLI for AI features
|
|
7644
7809
|
|
|
7645
|
-
|
|
7646
|
-
|
|
7810
|
+
Datacore:
|
|
7811
|
+
- ~/Data exists
|
|
7812
|
+
- .datacore/ configured
|
|
7813
|
+
- Spaces detected
|
|
7647
7814
|
|
|
7648
7815
|
Options:
|
|
7649
7816
|
--format json Output as JSON
|
|
7650
7817
|
|
|
7651
7818
|
Examples:
|
|
7652
|
-
datacore
|
|
7653
|
-
|
|
7819
|
+
datacore doctor
|
|
7820
|
+
datacore doctor --format json`);
|
|
7821
|
+
}
|
|
7822
|
+
function showIngestHelp() {
|
|
7823
|
+
console.log(`datacore ingest - Import files with AI processing
|
|
7654
7824
|
|
|
7655
7825
|
Usage:
|
|
7656
|
-
datacore
|
|
7826
|
+
datacore ingest <path> [options]
|
|
7657
7827
|
|
|
7658
7828
|
Description:
|
|
7659
|
-
|
|
7660
|
-
|
|
7829
|
+
Imports files into Datacore with deep knowledge extraction.
|
|
7830
|
+
The CLI validates paths and invokes the ingest-coordinator agent
|
|
7831
|
+
which handles the 6-phase semantic processing:
|
|
7661
7832
|
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7833
|
+
1. READ - Analyze content
|
|
7834
|
+
2. ASSESS - Determine destination
|
|
7835
|
+
3. EXTRACT - Pull out knowledge (zettels, insights)
|
|
7836
|
+
4. CAPTURE - Log tasks and TODOs
|
|
7837
|
+
5. FILE - Move to semantic location
|
|
7838
|
+
6. LINK - Connect to knowledge graph
|
|
7665
7839
|
|
|
7666
|
-
|
|
7667
|
-
|
|
7840
|
+
Options:
|
|
7841
|
+
--space <name> Target space (default: 0-personal)
|
|
7842
|
+
--dry-run Show what would happen without doing it
|
|
7668
7843
|
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7844
|
+
Examples:
|
|
7845
|
+
# Import a file
|
|
7846
|
+
datacore ingest ~/Documents/report.pdf
|
|
7672
7847
|
|
|
7673
|
-
|
|
7674
|
-
|
|
7848
|
+
# Import a folder
|
|
7849
|
+
datacore ingest ~/Documents/export/
|
|
7675
7850
|
|
|
7676
|
-
|
|
7677
|
-
datacore
|
|
7678
|
-
|
|
7679
|
-
|
|
7851
|
+
# Target specific space
|
|
7852
|
+
datacore ingest ~/Documents/contracts/ --space 1-datafund`);
|
|
7853
|
+
}
|
|
7854
|
+
function showSyncHelp() {
|
|
7855
|
+
console.log(`datacore sync - Git sync for all repos
|
|
7680
7856
|
|
|
7681
7857
|
Usage:
|
|
7682
|
-
datacore
|
|
7858
|
+
datacore sync [action] [options]
|
|
7859
|
+
|
|
7860
|
+
Actions:
|
|
7861
|
+
(none) Pull all repos (default)
|
|
7862
|
+
push Commit and push changes
|
|
7863
|
+
status Show git status overview
|
|
7683
7864
|
|
|
7684
7865
|
Description:
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
- Linux: crontab entry
|
|
7866
|
+
Manages git synchronization for the root Datacore repo
|
|
7867
|
+
and all space repositories.
|
|
7688
7868
|
|
|
7689
7869
|
Options:
|
|
7690
|
-
--
|
|
7870
|
+
--message <msg> Commit message (for push)
|
|
7691
7871
|
|
|
7692
7872
|
Examples:
|
|
7693
|
-
|
|
7694
|
-
datacore
|
|
7695
|
-
|
|
7873
|
+
# Pull latest changes
|
|
7874
|
+
datacore sync
|
|
7875
|
+
|
|
7876
|
+
# Push changes
|
|
7877
|
+
datacore sync push --message "Daily update"
|
|
7878
|
+
|
|
7879
|
+
# Check status
|
|
7880
|
+
datacore sync status`);
|
|
7881
|
+
}
|
|
7882
|
+
function showGtdMetaHelp(command) {
|
|
7883
|
+
if (command === "today") {
|
|
7884
|
+
console.log(`datacore today - Generate daily briefing
|
|
7696
7885
|
|
|
7697
7886
|
Usage:
|
|
7698
|
-
datacore
|
|
7887
|
+
datacore today [options]
|
|
7699
7888
|
|
|
7700
7889
|
Description:
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
-
|
|
7890
|
+
Generates your daily briefing by invoking the /today command.
|
|
7891
|
+
Includes:
|
|
7892
|
+
- Calendar events
|
|
7893
|
+
- Priority tasks
|
|
7894
|
+
- Nightshift results (completed AI tasks)
|
|
7895
|
+
- Journal entry
|
|
7896
|
+
|
|
7897
|
+
Options:
|
|
7898
|
+
--format json Output as JSON
|
|
7899
|
+
|
|
7900
|
+
Examples:
|
|
7901
|
+
datacore today`);
|
|
7902
|
+
} else {
|
|
7903
|
+
console.log(`datacore tomorrow - End-of-day wrap-up
|
|
7904
|
+
|
|
7905
|
+
Usage:
|
|
7906
|
+
datacore tomorrow [options]
|
|
7907
|
+
|
|
7908
|
+
Description:
|
|
7909
|
+
Wraps up your day by invoking the /tomorrow command.
|
|
7910
|
+
Includes:
|
|
7911
|
+
- Session summary
|
|
7912
|
+
- Queuing AI tasks for overnight
|
|
7913
|
+
- Learning extraction
|
|
7914
|
+
- Journal update
|
|
7915
|
+
|
|
7916
|
+
Options:
|
|
7917
|
+
--format json Output as JSON
|
|
7918
|
+
|
|
7919
|
+
Examples:
|
|
7920
|
+
datacore tomorrow`);
|
|
7921
|
+
}
|
|
7922
|
+
}
|
|
7923
|
+
function showActionHelp(resource, action) {
|
|
7924
|
+
if (!RESOURCES.includes(resource)) {
|
|
7925
|
+
console.log(`Unknown resource: ${resource}`);
|
|
7926
|
+
return;
|
|
7927
|
+
}
|
|
7928
|
+
const actions = ACTIONS[resource];
|
|
7929
|
+
if (!actions.includes(action)) {
|
|
7930
|
+
console.log(`Unknown action: ${action} for ${resource}
|
|
7931
|
+
|
|
7932
|
+
Available actions: ${actions.join(", ")}`);
|
|
7933
|
+
return;
|
|
7934
|
+
}
|
|
7935
|
+
const help = getDetailedHelp(resource, action);
|
|
7936
|
+
console.log(help);
|
|
7937
|
+
}
|
|
7938
|
+
function getResourceDescription(resource) {
|
|
7939
|
+
const descriptions = {
|
|
7940
|
+
space: "Create and list spaces",
|
|
7941
|
+
module: "Install and manage modules",
|
|
7942
|
+
config: "View and modify settings",
|
|
7943
|
+
nightshift: "Queue and trigger AI tasks",
|
|
7944
|
+
cron: "Manage scheduled tasks",
|
|
7945
|
+
snapshot: "Create and restore installation snapshots"
|
|
7946
|
+
};
|
|
7947
|
+
return descriptions[resource];
|
|
7948
|
+
}
|
|
7949
|
+
function getActionDescription(resource, action) {
|
|
7950
|
+
const descriptions = {
|
|
7951
|
+
"space create": "Create a new space",
|
|
7952
|
+
"space list": "List all spaces",
|
|
7953
|
+
"module install": "Install a module",
|
|
7954
|
+
"module list": "List installed modules",
|
|
7955
|
+
"module update": "Update all modules",
|
|
7956
|
+
"module remove": "Remove a module",
|
|
7957
|
+
"config show": "Show all settings",
|
|
7958
|
+
"config get": "Get a setting value",
|
|
7959
|
+
"config set": "Set a setting value",
|
|
7960
|
+
"nightshift status": "Show queue and server status",
|
|
7961
|
+
"nightshift trigger": "Execute queued AI tasks",
|
|
7962
|
+
"nightshift queue": "Add task to AI queue",
|
|
7963
|
+
"cron install": "Install scheduled jobs",
|
|
7964
|
+
"cron status": "Show job status",
|
|
7965
|
+
"cron remove": "Remove scheduled jobs",
|
|
7966
|
+
"snapshot create": "Create installation snapshot",
|
|
7967
|
+
"snapshot restore": "Restore from snapshot",
|
|
7968
|
+
"snapshot diff": "Compare with snapshot",
|
|
7969
|
+
"snapshot show": "Show snapshot contents"
|
|
7970
|
+
};
|
|
7971
|
+
return descriptions[`${resource} ${action}`] ?? action;
|
|
7972
|
+
}
|
|
7973
|
+
function getDetailedHelp(resource, action) {
|
|
7974
|
+
const key = `${resource} ${action}`;
|
|
7975
|
+
const helpTexts = {
|
|
7976
|
+
"space create": `datacore space create - Create a new space
|
|
7977
|
+
|
|
7978
|
+
Usage:
|
|
7979
|
+
datacore space create [options]
|
|
7980
|
+
|
|
7981
|
+
Description:
|
|
7982
|
+
Interactive wizard to create a new team or personal space.
|
|
7983
|
+
Invokes the create-space agent for semantic decisions.
|
|
7984
|
+
|
|
7985
|
+
Options:
|
|
7986
|
+
--name <name> Space name (lowercase, hyphenated)
|
|
7987
|
+
--type <type> Space type: team or personal
|
|
7988
|
+
--yes, -y Use defaults
|
|
7989
|
+
|
|
7990
|
+
Examples:
|
|
7991
|
+
datacore space create
|
|
7992
|
+
datacore space create --name fds --type team`,
|
|
7993
|
+
"space list": `datacore space list - List all spaces
|
|
7994
|
+
|
|
7995
|
+
Usage:
|
|
7996
|
+
datacore space list [options]
|
|
7997
|
+
|
|
7998
|
+
Description:
|
|
7999
|
+
Lists all spaces in your Datacore installation.
|
|
8000
|
+
|
|
8001
|
+
Options:
|
|
8002
|
+
--format json Output as JSON
|
|
8003
|
+
|
|
8004
|
+
Examples:
|
|
8005
|
+
datacore space list`,
|
|
8006
|
+
"module install": `datacore module install - Install a module
|
|
8007
|
+
|
|
8008
|
+
Usage:
|
|
8009
|
+
datacore module install <name> [options]
|
|
8010
|
+
|
|
8011
|
+
Description:
|
|
8012
|
+
Clones a module from the Datacore catalog and registers
|
|
8013
|
+
its agents and commands.
|
|
8014
|
+
|
|
8015
|
+
Arguments:
|
|
8016
|
+
<name> Module name (e.g., nightshift)
|
|
8017
|
+
|
|
8018
|
+
Examples:
|
|
8019
|
+
datacore module install nightshift
|
|
8020
|
+
datacore module install crm`,
|
|
8021
|
+
"module list": `datacore module list - List modules
|
|
8022
|
+
|
|
8023
|
+
Usage:
|
|
8024
|
+
datacore module list [options]
|
|
8025
|
+
|
|
8026
|
+
Description:
|
|
8027
|
+
Shows installed modules and available modules from catalog.
|
|
8028
|
+
|
|
8029
|
+
Options:
|
|
8030
|
+
--available Show only available (not installed)
|
|
8031
|
+
--format json Output as JSON
|
|
8032
|
+
|
|
8033
|
+
Examples:
|
|
8034
|
+
datacore module list
|
|
8035
|
+
datacore module list --available`,
|
|
8036
|
+
"config show": `datacore config show - Show all settings
|
|
8037
|
+
|
|
8038
|
+
Usage:
|
|
8039
|
+
datacore config show [options]
|
|
8040
|
+
|
|
8041
|
+
Description:
|
|
8042
|
+
Displays merged configuration from base and local settings.
|
|
8043
|
+
|
|
8044
|
+
Options:
|
|
8045
|
+
--format json Output as JSON
|
|
8046
|
+
|
|
8047
|
+
Examples:
|
|
8048
|
+
datacore config show`,
|
|
8049
|
+
"config get": `datacore config get - Get a setting value
|
|
8050
|
+
|
|
8051
|
+
Usage:
|
|
8052
|
+
datacore config get <key>
|
|
8053
|
+
|
|
8054
|
+
Arguments:
|
|
8055
|
+
<key> Setting key (e.g., sync.pull_on_today)
|
|
8056
|
+
|
|
8057
|
+
Examples:
|
|
8058
|
+
datacore config get sync.pull_on_today
|
|
8059
|
+
datacore config get editor.open_command`,
|
|
8060
|
+
"config set": `datacore config set - Set a setting value
|
|
8061
|
+
|
|
8062
|
+
Usage:
|
|
8063
|
+
datacore config set <key> <value>
|
|
8064
|
+
|
|
8065
|
+
Description:
|
|
8066
|
+
Updates settings.local.yaml with the new value.
|
|
8067
|
+
|
|
8068
|
+
Arguments:
|
|
8069
|
+
<key> Setting key
|
|
8070
|
+
<value> New value
|
|
8071
|
+
|
|
8072
|
+
Examples:
|
|
8073
|
+
datacore config set sync.pull_on_today false
|
|
8074
|
+
datacore config set editor.open_command code`,
|
|
8075
|
+
"nightshift status": `datacore nightshift status - Queue and server status
|
|
8076
|
+
|
|
8077
|
+
Usage:
|
|
8078
|
+
datacore nightshift status [options]
|
|
8079
|
+
|
|
8080
|
+
Description:
|
|
8081
|
+
Shows nightshift queue summary and server connection status.
|
|
8082
|
+
|
|
8083
|
+
Options:
|
|
8084
|
+
--format json Output as JSON
|
|
8085
|
+
|
|
8086
|
+
Examples:
|
|
8087
|
+
datacore nightshift status`,
|
|
8088
|
+
"nightshift trigger": `datacore nightshift trigger - Execute queued AI tasks
|
|
8089
|
+
|
|
8090
|
+
Usage:
|
|
8091
|
+
datacore nightshift trigger
|
|
8092
|
+
|
|
8093
|
+
Description:
|
|
8094
|
+
Manually triggers execution of queued AI tasks.
|
|
8095
|
+
Normally called by cron job at night.
|
|
8096
|
+
|
|
8097
|
+
Examples:
|
|
8098
|
+
datacore nightshift trigger`,
|
|
8099
|
+
"nightshift queue": `datacore nightshift queue - Add task to AI queue
|
|
8100
|
+
|
|
8101
|
+
Usage:
|
|
8102
|
+
datacore nightshift queue "<task description>"
|
|
8103
|
+
|
|
8104
|
+
Description:
|
|
8105
|
+
Quick way to add an AI task without editing org files.
|
|
8106
|
+
Adds entry to inbox.org with :AI: tag.
|
|
8107
|
+
|
|
8108
|
+
Arguments:
|
|
8109
|
+
<task> Task description
|
|
8110
|
+
|
|
8111
|
+
Examples:
|
|
8112
|
+
datacore nightshift queue "Research competitor analysis"
|
|
8113
|
+
datacore nightshift queue "Generate weekly metrics report"`,
|
|
8114
|
+
"cron install": `datacore cron install - Install scheduled jobs
|
|
8115
|
+
|
|
8116
|
+
Usage:
|
|
8117
|
+
datacore cron install [options]
|
|
8118
|
+
|
|
8119
|
+
Description:
|
|
8120
|
+
Sets up platform-specific scheduled tasks:
|
|
8121
|
+
- macOS: launchd plist
|
|
8122
|
+
- Linux: crontab entry
|
|
8123
|
+
|
|
8124
|
+
Options:
|
|
8125
|
+
--schedule <cron> Custom schedule (default: nightly)
|
|
8126
|
+
|
|
8127
|
+
Examples:
|
|
8128
|
+
datacore cron install
|
|
8129
|
+
datacore cron install --schedule "0 2 * * *"`,
|
|
8130
|
+
"snapshot create": `datacore snapshot create - Create installation snapshot
|
|
8131
|
+
|
|
8132
|
+
Usage:
|
|
8133
|
+
datacore snapshot create [path] [options]
|
|
8134
|
+
|
|
8135
|
+
Description:
|
|
8136
|
+
Creates a snapshot of your Datacore installation including:
|
|
8137
|
+
- Installed modules (with git commit hashes)
|
|
8138
|
+
- Spaces (with git sources)
|
|
7704
8139
|
- Dependency versions
|
|
7705
8140
|
- Optionally: base settings
|
|
7706
8141
|
|
|
@@ -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,11 +8538,28 @@ function checkPython(platform2) {
|
|
|
8096
8538
|
installCommand: !installed || !meetsMin ? getInstallCommand("python", platform2) ?? undefined : undefined
|
|
8097
8539
|
};
|
|
8098
8540
|
}
|
|
8099
|
-
function
|
|
8100
|
-
const installed = commandExists("
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
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
|
+
}
|
|
8558
|
+
function checkClaude(platform2) {
|
|
8559
|
+
const installed = commandExists("claude");
|
|
8560
|
+
return {
|
|
8561
|
+
name: "claude",
|
|
8562
|
+
required: false,
|
|
8104
8563
|
installed,
|
|
8105
8564
|
version: installed ? getVersion("claude", "--version") : undefined,
|
|
8106
8565
|
installCommand: installed ? undefined : getInstallCommand("claude", platform2) ?? undefined
|
|
@@ -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
|
}
|
|
@@ -8286,370 +8746,40 @@ function getAllConfig() {
|
|
|
8286
8746
|
let check = local;
|
|
8287
8747
|
for (const p of currentPath) {
|
|
8288
8748
|
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
|
-
"3-knowledge",
|
|
8380
|
-
"3-knowledge/pages",
|
|
8381
|
-
"3-knowledge/zettel",
|
|
8382
|
-
"3-knowledge/literature",
|
|
8383
|
-
"3-knowledge/reference",
|
|
8384
|
-
"4-archive",
|
|
8385
|
-
"content"
|
|
8386
|
-
] : [
|
|
8387
|
-
".datacore",
|
|
8388
|
-
".datacore/commands",
|
|
8389
|
-
".datacore/agents",
|
|
8390
|
-
".datacore/learning",
|
|
8391
|
-
".datacore/state",
|
|
8392
|
-
".datacore/env",
|
|
8393
|
-
"org",
|
|
8394
|
-
"0-inbox",
|
|
8395
|
-
"journal",
|
|
8396
|
-
"1-tracks",
|
|
8397
|
-
"1-tracks/ops",
|
|
8398
|
-
"1-tracks/product",
|
|
8399
|
-
"1-tracks/dev",
|
|
8400
|
-
"1-tracks/research",
|
|
8401
|
-
"1-tracks/comms",
|
|
8402
|
-
"2-projects",
|
|
8403
|
-
"3-knowledge",
|
|
8404
|
-
"3-knowledge/pages",
|
|
8405
|
-
"3-knowledge/zettel",
|
|
8406
|
-
"3-knowledge/literature",
|
|
8407
|
-
"3-knowledge/reference",
|
|
8408
|
-
"4-archive"
|
|
8409
|
-
];
|
|
8410
|
-
for (const dir of dirs) {
|
|
8411
|
-
mkdirSync2(join3(spacePath, dir), { recursive: true });
|
|
8412
|
-
}
|
|
8413
|
-
if (type === "personal") {
|
|
8414
|
-
writeFileSync2(join3(spacePath, "org", "inbox.org"), `#+TITLE: Inbox
|
|
8415
|
-
#+FILETAGS: :inbox:
|
|
8416
|
-
|
|
8417
|
-
Capture everything here. Process daily to zero.
|
|
8418
|
-
|
|
8419
|
-
* TODO Do more. With less.
|
|
8420
|
-
* Inbox
|
|
8421
|
-
`);
|
|
8422
|
-
writeFileSync2(join3(spacePath, "org", "next_actions.org"), `#+TITLE: Next Actions
|
|
8423
|
-
#+TODO: TODO NEXT WAITING | DONE CANCELED
|
|
8424
|
-
#+FILETAGS: :tasks:
|
|
8425
|
-
|
|
8426
|
-
Tasks organized by focus area. Tag with :AI: to delegate to agents.
|
|
8427
|
-
|
|
8428
|
-
* TIER 1: STRATEGIC FOUNDATION
|
|
8429
|
-
** /Projects
|
|
8430
|
-
** /Work
|
|
8431
|
-
* TIER 2: SUPPORTING WORK
|
|
8432
|
-
** Admin
|
|
8433
|
-
** Maintenance
|
|
8434
|
-
* PERSONAL: LIFE & DEVELOPMENT
|
|
8435
|
-
** /Personal Development
|
|
8436
|
-
** /Health & Longevity
|
|
8437
|
-
** Home & Family
|
|
8438
|
-
** Financial Management
|
|
8439
|
-
* RESEARCH & LEARNING
|
|
8440
|
-
** Technology
|
|
8441
|
-
** Skills
|
|
8442
|
-
`);
|
|
8443
|
-
writeFileSync2(join3(spacePath, "org", "nightshift.org"), `#+TITLE: Nightshift Queue
|
|
8444
|
-
#+TODO: QUEUED EXECUTING | DONE FAILED
|
|
8445
|
-
#+FILETAGS: :nightshift:
|
|
8446
|
-
|
|
8447
|
-
AI tasks queued for overnight execution. Managed by /tomorrow command.
|
|
8448
|
-
|
|
8449
|
-
* Queue
|
|
8450
|
-
`);
|
|
8451
|
-
writeFileSync2(join3(spacePath, "org", "habits.org"), `#+TITLE: Habits
|
|
8452
|
-
#+FILETAGS: :habits:
|
|
8453
|
-
|
|
8454
|
-
Recurring behaviors and routines. Track with org-habit.
|
|
8455
|
-
|
|
8456
|
-
* Daily
|
|
8457
|
-
* Weekly
|
|
8458
|
-
* Monthly
|
|
8459
|
-
`);
|
|
8460
|
-
writeFileSync2(join3(spacePath, "org", "someday.org"), `#+TITLE: Someday/Maybe
|
|
8461
|
-
#+FILETAGS: :someday:
|
|
8462
|
-
|
|
8463
|
-
Ideas and projects for the future. Review monthly.
|
|
8464
|
-
|
|
8465
|
-
* Someday
|
|
8466
|
-
* Maybe
|
|
8467
|
-
`);
|
|
8468
|
-
writeFileSync2(join3(spacePath, "org", "archive.org"), `#+TITLE: Archive
|
|
8469
|
-
#+FILETAGS: :archive:
|
|
8470
|
-
|
|
8471
|
-
Completed and canceled tasks. Searchable history.
|
|
8472
|
-
|
|
8473
|
-
* Archived Tasks
|
|
8474
|
-
`);
|
|
8475
|
-
} else {
|
|
8476
|
-
writeFileSync2(join3(spacePath, "org", "inbox.org"), `#+TITLE: Inbox
|
|
8477
|
-
#+FILETAGS: :inbox:
|
|
8478
|
-
|
|
8479
|
-
* Capture items here
|
|
8480
|
-
`);
|
|
8481
|
-
writeFileSync2(join3(spacePath, "org", "next_actions.org"), `#+TITLE: Next Actions
|
|
8482
|
-
#+FILETAGS: :tasks:
|
|
8483
|
-
|
|
8484
|
-
* Tasks
|
|
8485
|
-
** TODO items go here
|
|
8486
|
-
`);
|
|
8487
|
-
}
|
|
8488
|
-
if (type === "personal") {
|
|
8489
|
-
writeFileSync2(join3(spacePath, "_index.md"), `# Personal Space
|
|
8490
|
-
|
|
8491
|
-
Your personal knowledge base and GTD system.
|
|
8492
|
-
|
|
8493
|
-
## GTD Workflow
|
|
8494
|
-
|
|
8495
|
-
1. **Capture** → \`org/inbox.org\` - dump everything here
|
|
8496
|
-
2. **Clarify** → Is it actionable? What's the next action?
|
|
8497
|
-
3. **Organize** → Move to \`next_actions.org\` by focus area
|
|
8498
|
-
4. **Reflect** → Weekly review, monthly strategic
|
|
8499
|
-
5. **Engage** → Do the work, delegate with :AI: tags
|
|
8500
|
-
|
|
8501
|
-
## Org Files (GTD)
|
|
8502
|
-
|
|
8503
|
-
| File | Purpose |
|
|
8504
|
-
|------|---------|
|
|
8505
|
-
| [inbox.org](org/inbox.org) | Single capture point - process to zero daily |
|
|
8506
|
-
| [next_actions.org](org/next_actions.org) | Active tasks by focus area |
|
|
8507
|
-
| [nightshift.org](org/nightshift.org) | AI task queue (overnight processing) |
|
|
8508
|
-
| [habits.org](org/habits.org) | Recurring behaviors |
|
|
8509
|
-
| [someday.org](org/someday.org) | Future possibilities |
|
|
8510
|
-
| [archive.org](org/archive.org) | Completed/canceled tasks |
|
|
8511
|
-
|
|
8512
|
-
## Knowledge
|
|
8513
|
-
|
|
8514
|
-
| Folder | Purpose |
|
|
8515
|
-
|--------|---------|
|
|
8516
|
-
| [notes/](notes/) | Personal knowledge base (Obsidian) |
|
|
8517
|
-
| [notes/journals/](notes/journals/) | Daily personal journals |
|
|
8518
|
-
| [3-knowledge/](3-knowledge/) | Structured knowledge (zettelkasten) |
|
|
8519
|
-
|
|
8520
|
-
## Other
|
|
8521
|
-
|
|
8522
|
-
- [journal/](journal/) - AI session journals (auto-generated)
|
|
8523
|
-
- [content/](content/) - Generated content (drafts, emails)
|
|
8524
|
-
- [0-inbox/](0-inbox/) - File inbox (process files here)
|
|
8525
|
-
- [4-archive/](4-archive/) - Historical content
|
|
8526
|
-
`);
|
|
8527
|
-
} else {
|
|
8528
|
-
writeFileSync2(join3(spacePath, "_index.md"), `# ${name}
|
|
8529
|
-
|
|
8530
|
-
Team space.
|
|
8531
|
-
|
|
8532
|
-
## Structure
|
|
8533
|
-
|
|
8534
|
-
- \`org/\` - Task coordination
|
|
8535
|
-
- \`1-tracks/\` - Department work
|
|
8536
|
-
- \`2-projects/\` - Code repositories
|
|
8537
|
-
- \`3-knowledge/\` - Shared knowledge
|
|
8538
|
-
- \`4-archive/\` - Historical content
|
|
8539
|
-
|
|
8540
|
-
## Quick Links
|
|
8541
|
-
|
|
8542
|
-
- [Inbox](org/inbox.org)
|
|
8543
|
-
- [Tasks](org/next_actions.org)
|
|
8544
|
-
- [Journal](journal/)
|
|
8545
|
-
- [Knowledge](3-knowledge/)
|
|
8546
|
-
`);
|
|
8547
|
-
}
|
|
8548
|
-
if (type === "personal") {
|
|
8549
|
-
writeFileSync2(join3(spacePath, "CLAUDE.base.md"), `# Personal Space
|
|
8550
|
-
|
|
8551
|
-
Personal GTD system and knowledge base (per DIP-0009).
|
|
8552
|
-
|
|
8553
|
-
## GTD Files
|
|
8554
|
-
|
|
8555
|
-
| File | Purpose | Process |
|
|
8556
|
-
|------|---------|---------|
|
|
8557
|
-
| \`org/inbox.org\` | Single capture point | Process to zero daily |
|
|
8558
|
-
| \`org/next_actions.org\` | Active tasks by focus area | Work from here |
|
|
8559
|
-
| \`org/nightshift.org\` | AI task queue | Auto-managed by /tomorrow |
|
|
8560
|
-
| \`org/habits.org\` | Recurring behaviors | Track daily |
|
|
8561
|
-
| \`org/someday.org\` | Future possibilities | Review monthly |
|
|
8562
|
-
| \`org/archive.org\` | Completed tasks | Searchable history |
|
|
8563
|
-
|
|
8564
|
-
## Task States
|
|
8565
|
-
|
|
8566
|
-
| State | Meaning |
|
|
8567
|
-
|-------|---------|
|
|
8568
|
-
| \`TODO\` | Standard next action |
|
|
8569
|
-
| \`NEXT\` | High priority, work today |
|
|
8570
|
-
| \`WAITING\` | Blocked on external |
|
|
8571
|
-
| \`DONE\` | Completed (terminal) |
|
|
8572
|
-
| \`CANCELED\` | Will not do (terminal) |
|
|
8573
|
-
|
|
8574
|
-
## AI Delegation
|
|
8575
|
-
|
|
8576
|
-
Tag tasks with \`:AI:\` to delegate to agents:
|
|
8577
|
-
|
|
8578
|
-
| Tag | Agent | Autonomous |
|
|
8579
|
-
|-----|-------|------------|
|
|
8580
|
-
| \`:AI:content:\` | gtd-content-writer | Yes |
|
|
8581
|
-
| \`:AI:research:\` | gtd-research-processor | Yes |
|
|
8582
|
-
| \`:AI:data:\` | gtd-data-analyzer | Yes |
|
|
8583
|
-
| \`:AI:pm:\` | gtd-project-manager | Yes |
|
|
8584
|
-
|
|
8585
|
-
## Knowledge Structure
|
|
8586
|
-
|
|
8587
|
-
| Location | Purpose |
|
|
8588
|
-
|----------|---------|
|
|
8589
|
-
| \`notes/\` | Personal knowledge base (Obsidian) |
|
|
8590
|
-
| \`notes/journals/\` | Daily personal reflections |
|
|
8591
|
-
| \`3-knowledge/zettel/\` | Atomic concept notes |
|
|
8592
|
-
| \`3-knowledge/literature/\` | Source summaries |
|
|
8593
|
-
|
|
8594
|
-
## Daily Workflow
|
|
8595
|
-
|
|
8596
|
-
1. **Morning** - Run \`/today\` for briefing
|
|
8597
|
-
2. **Capture** - Everything to inbox.org
|
|
8598
|
-
3. **Process** - Clear inbox, route tasks
|
|
8599
|
-
4. **Work** - Focus on NEXT items
|
|
8600
|
-
5. **Evening** - Run \`/tomorrow\` for wrap-up
|
|
8601
|
-
|
|
8602
|
-
## See Also
|
|
8603
|
-
|
|
8604
|
-
Parent: ~/Data/CLAUDE.md
|
|
8605
|
-
`);
|
|
8606
|
-
} else {
|
|
8607
|
-
writeFileSync2(join3(spacePath, "CLAUDE.base.md"), `# ${name} Space
|
|
8608
|
-
|
|
8609
|
-
Team space for ${name}.
|
|
8610
|
-
|
|
8611
|
-
## Structure
|
|
8612
|
-
|
|
8613
|
-
See parent CLAUDE.md for full documentation.
|
|
8614
|
-
`);
|
|
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
|
+
}
|
|
8615
8773
|
}
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
type: ${type}
|
|
8619
|
-
`);
|
|
8620
|
-
writeFileSync2(join3(spacePath, ".datacore", "learning", "patterns.md"), `# Patterns
|
|
8621
|
-
|
|
8622
|
-
Successful approaches to remember.
|
|
8623
|
-
`);
|
|
8624
|
-
writeFileSync2(join3(spacePath, ".datacore", "learning", "corrections.md"), `# Corrections
|
|
8625
|
-
|
|
8626
|
-
Human feedback log.
|
|
8627
|
-
`);
|
|
8628
|
-
writeFileSync2(join3(spacePath, ".datacore", "learning", "preferences.md"), `# Preferences
|
|
8629
|
-
|
|
8630
|
-
Style and preference notes.
|
|
8631
|
-
`);
|
|
8632
|
-
writeFileSync2(join3(spacePath, ".gitignore"), type === "personal" ? `.datacore/state/
|
|
8633
|
-
.datacore/env/
|
|
8634
|
-
CLAUDE.md
|
|
8635
|
-
CLAUDE.local.md
|
|
8636
|
-
` : `.datacore/state/
|
|
8637
|
-
.datacore/env/
|
|
8638
|
-
CLAUDE.md
|
|
8639
|
-
CLAUDE.local.md
|
|
8640
|
-
2-projects/
|
|
8641
|
-
`);
|
|
8642
|
-
return {
|
|
8643
|
-
name: folderName,
|
|
8644
|
-
number,
|
|
8645
|
-
path: spacePath,
|
|
8646
|
-
type,
|
|
8647
|
-
hasGit: false,
|
|
8648
|
-
hasClaude: true
|
|
8649
|
-
};
|
|
8774
|
+
traceSources(merged);
|
|
8775
|
+
return { merged, sources };
|
|
8650
8776
|
}
|
|
8651
8777
|
|
|
8778
|
+
// src/index.ts
|
|
8779
|
+
init_space();
|
|
8780
|
+
|
|
8652
8781
|
// src/lib/sync.ts
|
|
8782
|
+
init_space();
|
|
8653
8783
|
import { execSync as execSync3 } from "child_process";
|
|
8654
8784
|
import { existsSync as existsSync4 } from "fs";
|
|
8655
8785
|
import { join as join4, basename as basename2 } from "path";
|
|
@@ -8784,46 +8914,89 @@ function getGitRepos() {
|
|
|
8784
8914
|
}
|
|
8785
8915
|
|
|
8786
8916
|
// src/lib/init.ts
|
|
8787
|
-
import { existsSync as
|
|
8788
|
-
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";
|
|
8789
8920
|
import { createInterface } from "readline";
|
|
8790
8921
|
|
|
8791
8922
|
// src/lib/module.ts
|
|
8792
8923
|
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync } from "fs";
|
|
8793
8924
|
import { join as join5, basename as basename3 } from "path";
|
|
8794
|
-
import {
|
|
8795
|
-
var DATA_DIR4 = join5(process.env.HOME || "
|
|
8925
|
+
import { execFileSync } from "child_process";
|
|
8926
|
+
var DATA_DIR4 = join5(process.env.HOME || "", "Data");
|
|
8796
8927
|
var MODULES_DIR = join5(DATA_DIR4, ".datacore", "modules");
|
|
8797
8928
|
var AVAILABLE_MODULES = [
|
|
8798
8929
|
{
|
|
8799
8930
|
name: "nightshift",
|
|
8800
|
-
description: "
|
|
8801
|
-
repo: "https://github.com/datacore-one/
|
|
8802
|
-
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
|
|
8803
8942
|
},
|
|
8804
8943
|
{
|
|
8805
8944
|
name: "crm",
|
|
8806
|
-
description: "
|
|
8807
|
-
repo: "https://github.com/datacore-one/
|
|
8808
|
-
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
|
|
8809
8949
|
},
|
|
8810
8950
|
{
|
|
8811
8951
|
name: "meetings",
|
|
8812
|
-
description: "Meeting
|
|
8813
|
-
repo: "https://github.com/datacore-one/
|
|
8814
|
-
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
|
|
8815
8956
|
},
|
|
8816
8957
|
{
|
|
8817
|
-
name: "
|
|
8818
|
-
description: "
|
|
8819
|
-
repo: "https://github.com/datacore-one/
|
|
8820
|
-
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
|
|
8821
8998
|
}
|
|
8822
8999
|
];
|
|
8823
|
-
function getAvailableModules() {
|
|
8824
|
-
const installed = listModules().map((m) => m.name);
|
|
8825
|
-
return AVAILABLE_MODULES.filter((m) => !installed.includes(m.name));
|
|
8826
|
-
}
|
|
8827
9000
|
function listModules() {
|
|
8828
9001
|
if (!existsSync5(MODULES_DIR)) {
|
|
8829
9002
|
return [];
|
|
@@ -8869,108 +9042,362 @@ function getModuleInfo(modulePath) {
|
|
|
8869
9042
|
}
|
|
8870
9043
|
}
|
|
8871
9044
|
}
|
|
8872
|
-
const commandsDir = join5(modulePath, "commands");
|
|
8873
|
-
const commands = [];
|
|
8874
|
-
if (existsSync5(commandsDir)) {
|
|
8875
|
-
const cmdFiles = readdirSync2(commandsDir);
|
|
8876
|
-
for (const file of cmdFiles) {
|
|
8877
|
-
if (file.endsWith(".md")) {
|
|
8878
|
-
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
|
+
});
|
|
8879
9279
|
}
|
|
8880
9280
|
}
|
|
8881
9281
|
}
|
|
8882
|
-
|
|
8883
|
-
name
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
description,
|
|
8887
|
-
agents,
|
|
8888
|
-
commands
|
|
8889
|
-
};
|
|
8890
|
-
}
|
|
8891
|
-
function installModule(source) {
|
|
8892
|
-
if (!existsSync5(MODULES_DIR)) {
|
|
8893
|
-
const { mkdirSync: mkdirSync3 } = __require("fs");
|
|
8894
|
-
mkdirSync3(MODULES_DIR, { recursive: true });
|
|
8895
|
-
}
|
|
8896
|
-
let name;
|
|
8897
|
-
let isGit = false;
|
|
8898
|
-
if (source.includes("github.com") || source.startsWith("git@") || source.endsWith(".git")) {
|
|
8899
|
-
isGit = true;
|
|
8900
|
-
const match = source.match(/([^/]+?)(?:\.git)?$/);
|
|
8901
|
-
name = match?.[1] ?? source.split("/").pop() ?? "unknown";
|
|
8902
|
-
name = name.replace(/^module-/, "");
|
|
8903
|
-
} else if (source.startsWith("@")) {
|
|
8904
|
-
name = source.split("/").pop()?.replace(/^datacore-/, "") ?? "unknown";
|
|
8905
|
-
} else {
|
|
8906
|
-
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
|
+
}
|
|
8907
9286
|
}
|
|
8908
|
-
const
|
|
8909
|
-
|
|
8910
|
-
|
|
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
|
+
}
|
|
8911
9293
|
}
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
const gitUrl = `https://github.com/datacore/module-${name}.git`;
|
|
8916
|
-
try {
|
|
8917
|
-
execSync4(`git clone ${gitUrl} "${modulePath}"`, { stdio: "pipe" });
|
|
8918
|
-
} catch {
|
|
8919
|
-
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);
|
|
8920
9297
|
}
|
|
8921
9298
|
}
|
|
8922
|
-
const
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
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
|
+
}
|
|
8926
9309
|
}
|
|
8927
|
-
return
|
|
9310
|
+
return diff;
|
|
8928
9311
|
}
|
|
8929
|
-
function
|
|
8930
|
-
const modules =
|
|
8931
|
-
const
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
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
|
+
});
|
|
8941
9352
|
}
|
|
8942
|
-
} catch (err) {
|
|
8943
|
-
results.push({ name: mod.name, updated: false, error: err.message });
|
|
8944
9353
|
}
|
|
8945
9354
|
}
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
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
|
+
}
|
|
8952
9380
|
}
|
|
8953
|
-
|
|
8954
|
-
return true;
|
|
9381
|
+
return result;
|
|
8955
9382
|
}
|
|
8956
9383
|
|
|
8957
9384
|
// src/state.ts
|
|
8958
|
-
import { existsSync as
|
|
8959
|
-
import { join as
|
|
8960
|
-
var STATE_DIR =
|
|
8961
|
-
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");
|
|
8962
9389
|
function ensureStateDir() {
|
|
8963
|
-
if (!
|
|
8964
|
-
|
|
9390
|
+
if (!existsSync8(STATE_DIR)) {
|
|
9391
|
+
mkdirSync4(STATE_DIR, { recursive: true });
|
|
8965
9392
|
}
|
|
8966
9393
|
}
|
|
8967
9394
|
function loadState() {
|
|
8968
9395
|
ensureStateDir();
|
|
8969
|
-
if (!
|
|
9396
|
+
if (!existsSync8(STATE_FILE)) {
|
|
8970
9397
|
return { operations: [] };
|
|
8971
9398
|
}
|
|
8972
9399
|
try {
|
|
8973
|
-
const content =
|
|
9400
|
+
const content = readFileSync4(STATE_FILE, "utf-8");
|
|
8974
9401
|
return JSON.parse(content);
|
|
8975
9402
|
} catch {
|
|
8976
9403
|
return { operations: [] };
|
|
@@ -8978,7 +9405,7 @@ function loadState() {
|
|
|
8978
9405
|
}
|
|
8979
9406
|
function saveState(state) {
|
|
8980
9407
|
ensureStateDir();
|
|
8981
|
-
|
|
9408
|
+
writeFileSync4(STATE_FILE, JSON.stringify(state, null, 2));
|
|
8982
9409
|
}
|
|
8983
9410
|
function generateId() {
|
|
8984
9411
|
return `op_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -8995,7 +9422,8 @@ class Operation {
|
|
|
8995
9422
|
status: "pending",
|
|
8996
9423
|
startedAt: new Date().toISOString(),
|
|
8997
9424
|
steps: [],
|
|
8998
|
-
rollback: []
|
|
9425
|
+
rollback: [],
|
|
9426
|
+
meta: Object.keys(params).length > 0 ? params : undefined
|
|
8999
9427
|
};
|
|
9000
9428
|
this.store.operations.push(this.state);
|
|
9001
9429
|
saveState(this.store);
|
|
@@ -9087,16 +9515,12 @@ ${c.reset}${c.dim} AI-Powered Second Brain ${c.reset}
|
|
|
9087
9515
|
`;
|
|
9088
9516
|
var INIT_COMPLETE = `
|
|
9089
9517
|
${c.green}${c.bright}
|
|
9090
|
-
|
|
9091
|
-
║
|
|
9092
|
-
║ ${c.reset}${c.green}
|
|
9093
|
-
║ ${c.reset}${c.green}
|
|
9094
|
-
║
|
|
9095
|
-
|
|
9096
|
-
║ ${c.reset}${c.green}██║ ██║╚██████╗ ██║ ██║ ╚████╔╝ ███████╗${c.bright} ║
|
|
9097
|
-
║ ${c.reset}${c.green}╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═══╝ ╚══════╝${c.bright} ║
|
|
9098
|
-
║ ║
|
|
9099
|
-
╚═══════════════════════════════════════════════════════════════╝
|
|
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
|
+
╚══════════════════════════════════════════════════════════╝
|
|
9100
9524
|
${c.reset}
|
|
9101
9525
|
`;
|
|
9102
9526
|
function sleep(ms) {
|
|
@@ -9143,9 +9567,76 @@ function section(title) {
|
|
|
9143
9567
|
console.log(`${c.dim}${"─".repeat(50)}${c.reset}`);
|
|
9144
9568
|
}
|
|
9145
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
|
+
|
|
9146
9609
|
// src/lib/init.ts
|
|
9147
|
-
var
|
|
9148
|
-
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
|
+
];
|
|
9149
9640
|
var c2 = {
|
|
9150
9641
|
reset: "\x1B[0m",
|
|
9151
9642
|
bold: "\x1B[1m",
|
|
@@ -9169,20 +9660,220 @@ async function prompt(question, defaultValue) {
|
|
|
9169
9660
|
});
|
|
9170
9661
|
});
|
|
9171
9662
|
}
|
|
9172
|
-
async function confirm(question, defaultYes = true) {
|
|
9173
|
-
const hint = defaultYes ? "[Y/n]" : "[y/N]";
|
|
9174
|
-
const answer = await prompt(`${question} ${hint}`);
|
|
9175
|
-
if (!answer)
|
|
9176
|
-
return defaultYes;
|
|
9177
|
-
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 };
|
|
9178
9864
|
}
|
|
9179
9865
|
function isInitialized() {
|
|
9180
|
-
|
|
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
|
+
}
|
|
9181
9871
|
}
|
|
9182
9872
|
async function initDatacore(options = {}) {
|
|
9183
|
-
const { nonInteractive = false, skipChecks = false, stream = false } = options;
|
|
9873
|
+
const { nonInteractive = false, skipChecks = false, stream = false, verbose = false } = options;
|
|
9184
9874
|
const isTTY = stream && process.stdout.isTTY;
|
|
9185
9875
|
const interactive = isTTY && !nonInteractive;
|
|
9876
|
+
const platform2 = detectPlatform();
|
|
9186
9877
|
const result = {
|
|
9187
9878
|
success: false,
|
|
9188
9879
|
created: [],
|
|
@@ -9194,6 +9885,7 @@ async function initDatacore(options = {}) {
|
|
|
9194
9885
|
};
|
|
9195
9886
|
const op = startOperation("init", { options });
|
|
9196
9887
|
op.start();
|
|
9888
|
+
let profile = { name: "", email: "", useCase: "both", role: "" };
|
|
9197
9889
|
try {
|
|
9198
9890
|
if (isTTY) {
|
|
9199
9891
|
console.clear();
|
|
@@ -9202,388 +9894,1024 @@ async function initDatacore(options = {}) {
|
|
|
9202
9894
|
console.log();
|
|
9203
9895
|
await sleep(300);
|
|
9204
9896
|
}
|
|
9205
|
-
op.addStep("
|
|
9206
|
-
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");
|
|
9207
9935
|
if (!skipChecks) {
|
|
9208
9936
|
if (isTTY) {
|
|
9209
|
-
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}`);
|
|
9210
9941
|
console.log();
|
|
9211
9942
|
}
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
if (gitStatus.configured) {
|
|
9218
|
-
console.log(` ${c2.dim}Configured as: ${gitStatus.userName} <${gitStatus.userEmail}>${c2.reset}`);
|
|
9219
|
-
} else {
|
|
9220
|
-
console.log(` ${c2.yellow}⚠${c2.reset} ${c2.yellow}Not configured. Run:${c2.reset}`);
|
|
9221
|
-
console.log(` ${c2.dim}git config --global user.name "Your Name"${c2.reset}`);
|
|
9222
|
-
console.log(` ${c2.dim}git config --global user.email "you@example.com"${c2.reset}`);
|
|
9223
|
-
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();
|
|
9224
9948
|
}
|
|
9225
|
-
|
|
9226
|
-
console.log(` ${c2.red}✗${c2.reset} git ${c2.red}(required)${c2.reset}`);
|
|
9227
|
-
console.log(` ${c2.dim}Version control for your knowledge system${c2.reset}`);
|
|
9228
|
-
result.errors.push("git is required but not installed");
|
|
9949
|
+
await ensureHomebrew(!!isTTY);
|
|
9229
9950
|
}
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
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}`);
|
|
9235
9956
|
}
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
};
|
|
9245
|
-
if (dep.installed) {
|
|
9246
|
-
console.log(` ${c2.green}✓${c2.reset} ${dep.name} ${dep.version ? `${c2.dim}(${dep.version})${c2.reset}` : ""}`);
|
|
9247
|
-
} else if (dep.required) {
|
|
9248
|
-
console.log(` ${c2.red}✗${c2.reset} ${dep.name} ${c2.red}(required)${c2.reset}`);
|
|
9249
|
-
console.log(` ${c2.dim}${info2[dep.name] || ""}${c2.reset}`);
|
|
9250
|
-
if (dep.installCommand) {
|
|
9251
|
-
console.log(` ${c2.yellow}Install: ${dep.installCommand}${c2.reset}`);
|
|
9252
|
-
}
|
|
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}>`);
|
|
9253
9965
|
} else {
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
if (dep.installCommand) {
|
|
9257
|
-
console.log(` ${c2.dim}Install: ${dep.installCommand}${c2.reset}`);
|
|
9258
|
-
}
|
|
9966
|
+
runArgs("git", ["config", "--global", "user.name", profile.name]);
|
|
9967
|
+
runArgs("git", ["config", "--global", "user.email", profile.email]);
|
|
9259
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}>`);
|
|
9976
|
+
}
|
|
9977
|
+
} else if (gitConfig.configured && isTTY && !git.wasInstalled) {
|
|
9978
|
+
console.log(` ${c2.dim}Configured as: ${gitConfig.name} <${gitConfig.email}>${c2.reset}`);
|
|
9260
9979
|
}
|
|
9261
|
-
|
|
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}`);
|
|
9998
|
+
}
|
|
9999
|
+
if (!claude.available) {
|
|
10000
|
+
result.warnings.push("Claude Code not installed - install with: npm install -g @anthropic-ai/claude-code");
|
|
9262
10001
|
}
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
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}`);
|
|
9267
10016
|
}
|
|
9268
|
-
|
|
9269
|
-
|
|
10017
|
+
}
|
|
10018
|
+
if (commandExists3("gh")) {
|
|
10019
|
+
const ghAuth2 = checkGhAuth();
|
|
10020
|
+
if (!ghAuth2.available && interactive) {
|
|
9270
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
|
+
}
|
|
9271
10041
|
}
|
|
9272
|
-
op.failStep("check_dependencies", "Missing required dependencies");
|
|
9273
|
-
op.fail("Missing required dependencies");
|
|
9274
|
-
return result;
|
|
9275
10042
|
}
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
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}`);
|
|
9282
10053
|
}
|
|
10054
|
+
console.log();
|
|
9283
10055
|
}
|
|
9284
10056
|
}
|
|
9285
|
-
op.completeStep("
|
|
9286
|
-
op.addStep("
|
|
9287
|
-
op.startStep("
|
|
10057
|
+
op.completeStep("system_setup");
|
|
10058
|
+
op.addStep("clone_repo");
|
|
10059
|
+
op.startStep("clone_repo");
|
|
9288
10060
|
if (isTTY) {
|
|
9289
|
-
section(
|
|
9290
|
-
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}`);
|
|
9291
10065
|
console.log();
|
|
9292
10066
|
}
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
result.created.push(DATA_DIR5);
|
|
10067
|
+
const ghAuth = checkGhAuth();
|
|
10068
|
+
if (existsSync9(join10(DATA_DIR8, ".git"))) {
|
|
9296
10069
|
if (isTTY)
|
|
9297
|
-
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
|
+
}
|
|
9298
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"))) {
|
|
9299
10173
|
if (isTTY)
|
|
9300
|
-
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
|
+
}
|
|
9301
10185
|
}
|
|
9302
10186
|
if (isTTY)
|
|
9303
10187
|
console.log();
|
|
9304
|
-
op.completeStep("
|
|
9305
|
-
op.addStep("
|
|
9306
|
-
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");
|
|
9307
10297
|
if (isTTY) {
|
|
9308
|
-
section(
|
|
9309
|
-
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}`);
|
|
9310
10329
|
console.log();
|
|
9311
10330
|
}
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
{
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
{
|
|
9320
|
-
|
|
9321
|
-
{
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
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 });
|
|
9330
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`);
|
|
9331
10355
|
}
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
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(`
|
|
9353
10411
|
`);
|
|
9354
|
-
|
|
9355
|
-
|
|
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(`
|
|
9356
10431
|
`);
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
console.log(` ${c2.green}✓${c2.reset} Created settings.yaml`);
|
|
9360
|
-
console.log(` ${c2.dim}Customize in settings.local.yaml${c2.reset}`);
|
|
9361
|
-
}
|
|
9362
|
-
} else {
|
|
10432
|
+
writeFileSync5(settingsLocalPath, settingsContent);
|
|
10433
|
+
result.created.push(settingsLocalPath);
|
|
9363
10434
|
if (isTTY)
|
|
9364
|
-
console.log(` ${c2.green}✓${c2.reset}
|
|
10435
|
+
console.log(` ${c2.green}✓${c2.reset} settings.local.yaml created (your preferences)`);
|
|
9365
10436
|
}
|
|
9366
10437
|
if (isTTY)
|
|
9367
10438
|
console.log();
|
|
9368
|
-
op.completeStep("
|
|
9369
|
-
op.addStep("
|
|
9370
|
-
op.startStep("
|
|
10439
|
+
op.completeStep("second_brain");
|
|
10440
|
+
op.addStep("modules");
|
|
10441
|
+
op.startStep("modules");
|
|
9371
10442
|
if (isTTY) {
|
|
9372
|
-
section(
|
|
9373
|
-
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}`);
|
|
9374
10447
|
console.log();
|
|
9375
10448
|
}
|
|
9376
|
-
const
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
console.log(`
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
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
|
+
}
|
|
9399
10480
|
}
|
|
10481
|
+
modulesToInstall = allModules.filter((_, i) => selected.has(i));
|
|
9400
10482
|
} else {
|
|
9401
|
-
|
|
9402
|
-
|
|
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");
|
|
9403
10612
|
}
|
|
9404
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
|
+
}
|
|
9405
10622
|
if (isTTY)
|
|
9406
10623
|
console.log();
|
|
9407
|
-
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");
|
|
9408
10629
|
if (interactive) {
|
|
9409
|
-
section(
|
|
9410
|
-
console.log(` ${c2.dim}Spaces separate different areas of your life (work, projects, teams).${c2.reset}`);
|
|
9411
|
-
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`);
|
|
9412
10631
|
console.log();
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
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}`);
|
|
9421
10655
|
}
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
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
|
+
}
|
|
9429
10771
|
}
|
|
9430
|
-
creating = await confirm("Create another space?", false);
|
|
9431
10772
|
}
|
|
10773
|
+
console.log();
|
|
10774
|
+
importing = await confirm(" Import more?", false);
|
|
10775
|
+
if (importing)
|
|
10776
|
+
console.log();
|
|
9432
10777
|
}
|
|
9433
10778
|
console.log();
|
|
9434
10779
|
}
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
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`);
|
|
9438
10786
|
console.log();
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
const choices = await prompt('Enter numbers to install (comma-separated, or "all", or "none")');
|
|
9455
|
-
let toInstall = [];
|
|
9456
|
-
if (choices.toLowerCase() === "all") {
|
|
9457
|
-
toInstall = available.map((m) => m.name);
|
|
9458
|
-
} else if (choices && choices.toLowerCase() !== "none") {
|
|
9459
|
-
const nums = choices.split(",").map((s) => parseInt(s.trim(), 10));
|
|
9460
|
-
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}`);
|
|
9461
10802
|
}
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
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}`);
|
|
9477
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}`);
|
|
9478
10828
|
}
|
|
9479
|
-
} else {
|
|
9480
|
-
console.log(` ${c2.dim}No additional modules available to install.${c2.reset}`);
|
|
9481
10829
|
}
|
|
9482
10830
|
console.log();
|
|
9483
|
-
}
|
|
9484
|
-
|
|
9485
|
-
op.startStep("create_claude_symlink");
|
|
9486
|
-
if (isTTY) {
|
|
9487
|
-
section("Step 7: Claude Code Integration");
|
|
9488
|
-
console.log(` ${c2.dim}Connecting Datacore to Claude Code AI assistant.${c2.reset}`);
|
|
10831
|
+
} else if (isTTY) {
|
|
10832
|
+
section(`Step 9/${TOTAL_STEPS}: Verification`);
|
|
9489
10833
|
console.log();
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
if (!existsSync7(claudeDir)) {
|
|
9493
|
-
try {
|
|
9494
|
-
symlinkSync(DATACORE_DIR, claudeDir);
|
|
9495
|
-
result.created.push(claudeDir);
|
|
9496
|
-
if (isTTY)
|
|
9497
|
-
console.log(` ${c2.green}✓${c2.reset} Created .claude -> .datacore symlink`);
|
|
9498
|
-
} catch {
|
|
9499
|
-
result.warnings.push("Could not create .claude symlink");
|
|
9500
|
-
if (isTTY)
|
|
9501
|
-
console.log(` ${c2.yellow}○${c2.reset} Could not create symlink`);
|
|
9502
|
-
}
|
|
9503
|
-
} else {
|
|
9504
|
-
if (isTTY)
|
|
9505
|
-
console.log(` ${c2.green}✓${c2.reset} Found existing .claude/`);
|
|
9506
|
-
}
|
|
9507
|
-
const claudeMd = join7(DATA_DIR5, "CLAUDE.md");
|
|
9508
|
-
const claudeBaseMd = join7(DATA_DIR5, "CLAUDE.base.md");
|
|
9509
|
-
if (!existsSync7(claudeMd) && !existsSync7(claudeBaseMd)) {
|
|
9510
|
-
const allSpaces = listSpaces();
|
|
9511
|
-
writeFileSync4(claudeBaseMd, `# Datacore
|
|
9512
|
-
|
|
9513
|
-
AI-powered second brain built on GTD methodology.
|
|
9514
|
-
|
|
9515
|
-
## Quick Start
|
|
9516
|
-
|
|
9517
|
-
\`\`\`bash
|
|
9518
|
-
cd ~/Data && claude
|
|
9519
|
-
\`\`\`
|
|
9520
|
-
|
|
9521
|
-
## Daily Workflow
|
|
9522
|
-
|
|
9523
|
-
1. **Morning**: Run \`/today\` for your daily briefing
|
|
9524
|
-
2. **Capture**: Add tasks to \`0-personal/org/inbox.org\`
|
|
9525
|
-
3. **Process**: Clear inbox, organize by context
|
|
9526
|
-
4. **Work**: Use \`:AI:\` tags to delegate tasks
|
|
9527
|
-
5. **Evening**: Run \`/tomorrow\` for wrap-up
|
|
9528
|
-
|
|
9529
|
-
## Commands
|
|
9530
|
-
|
|
9531
|
-
- \`/today\` - Daily briefing with priorities
|
|
9532
|
-
- \`/tomorrow\` - End-of-day wrap-up
|
|
9533
|
-
- \`/sync\` - Sync all repos
|
|
9534
|
-
- \`datacore doctor\` - Check system health
|
|
9535
|
-
|
|
9536
|
-
## Spaces
|
|
9537
|
-
|
|
9538
|
-
${allSpaces.map((s) => `- ${s.name}`).join(`
|
|
9539
|
-
`) || "- 0-personal"}
|
|
9540
|
-
|
|
9541
|
-
## Documentation
|
|
9542
|
-
|
|
9543
|
-
See .datacore/specs/ for detailed documentation.
|
|
9544
|
-
`);
|
|
9545
|
-
result.created.push(claudeBaseMd);
|
|
9546
|
-
if (isTTY)
|
|
9547
|
-
console.log(` ${c2.green}✓${c2.reset} Created CLAUDE.base.md`);
|
|
9548
|
-
} else {
|
|
9549
|
-
if (isTTY)
|
|
9550
|
-
console.log(` ${c2.green}✓${c2.reset} Found existing CLAUDE.md`);
|
|
9551
|
-
}
|
|
9552
|
-
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}`);
|
|
9553
10836
|
console.log();
|
|
9554
|
-
|
|
10837
|
+
}
|
|
10838
|
+
op.completeStep("verification");
|
|
9555
10839
|
result.success = true;
|
|
9556
10840
|
result.nextSteps = [
|
|
9557
|
-
|
|
9558
|
-
"
|
|
9559
|
-
"
|
|
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"
|
|
9560
10845
|
];
|
|
9561
10846
|
if (isTTY) {
|
|
9562
10847
|
console.log(INIT_COMPLETE);
|
|
9563
10848
|
console.log();
|
|
9564
|
-
console.log(` ${c2.bold}Setup Complete!${c2.reset}`);
|
|
10849
|
+
console.log(` ${c2.bold}Setup Complete${profile.name ? `, ${profile.name}` : ""}!${c2.reset}`);
|
|
9565
10850
|
console.log();
|
|
9566
|
-
|
|
9567
|
-
|
|
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();
|
|
10859
|
+
}
|
|
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();
|
|
9568
10864
|
}
|
|
9569
|
-
|
|
9570
|
-
|
|
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();
|
|
9571
10874
|
}
|
|
9572
10875
|
if (result.warnings.length > 0) {
|
|
9573
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();
|
|
9574
10881
|
}
|
|
9575
|
-
console.log();
|
|
9576
10882
|
console.log(` ${c2.bold}Get Started:${c2.reset}`);
|
|
9577
10883
|
console.log();
|
|
9578
10884
|
console.log(` ${c2.cyan}1.${c2.reset} cd ~/Data && claude`);
|
|
9579
10885
|
console.log(` ${c2.dim}Start Claude Code in your Datacore directory${c2.reset}`);
|
|
9580
10886
|
console.log();
|
|
9581
10887
|
console.log(` ${c2.cyan}2.${c2.reset} Type ${c2.cyan}/today${c2.reset}`);
|
|
9582
|
-
console.log(` ${c2.dim}Get your first daily briefing${c2.reset}`);
|
|
9583
|
-
console.log();
|
|
9584
|
-
console.log(` ${c2.cyan}3.${c2.reset} Add tasks to ${c2.cyan}0-personal/org/inbox.org${c2.reset}`);
|
|
9585
|
-
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}`);
|
|
9586
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}`);
|
|
9587
10915
|
console.log(` ${c2.dim}Run 'datacore doctor' anytime to check system health.${c2.reset}`);
|
|
9588
10916
|
console.log();
|
|
9589
10917
|
}
|
|
@@ -9595,233 +10923,8 @@ See .datacore/specs/ for detailed documentation.
|
|
|
9595
10923
|
return result;
|
|
9596
10924
|
}
|
|
9597
10925
|
|
|
9598
|
-
// src/lib/snapshot.ts
|
|
9599
|
-
var import_yaml2 = __toESM(require_dist(), 1);
|
|
9600
|
-
import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
9601
|
-
import { execSync as execSync5 } from "child_process";
|
|
9602
|
-
import { join as join8 } from "path";
|
|
9603
|
-
var DATA_DIR6 = join8(process.env.HOME || "~", "Data");
|
|
9604
|
-
var LOCK_FILE = join8(DATA_DIR6, "datacore.lock.yaml");
|
|
9605
|
-
var CLI_VERSION = "1.0.6";
|
|
9606
|
-
function getGitInfo(path) {
|
|
9607
|
-
if (!existsSync8(join8(path, ".git"))) {
|
|
9608
|
-
return {};
|
|
9609
|
-
}
|
|
9610
|
-
try {
|
|
9611
|
-
const remote = execSync5("git remote get-url origin 2>/dev/null || true", {
|
|
9612
|
-
cwd: path,
|
|
9613
|
-
encoding: "utf-8"
|
|
9614
|
-
}).trim() || undefined;
|
|
9615
|
-
const commit = execSync5("git rev-parse HEAD 2>/dev/null || true", {
|
|
9616
|
-
cwd: path,
|
|
9617
|
-
encoding: "utf-8"
|
|
9618
|
-
}).trim() || undefined;
|
|
9619
|
-
const branch = execSync5("git branch --show-current 2>/dev/null || true", {
|
|
9620
|
-
cwd: path,
|
|
9621
|
-
encoding: "utf-8"
|
|
9622
|
-
}).trim() || undefined;
|
|
9623
|
-
return { remote, commit, branch };
|
|
9624
|
-
} catch {
|
|
9625
|
-
return {};
|
|
9626
|
-
}
|
|
9627
|
-
}
|
|
9628
|
-
function createSnapshot(options = {}) {
|
|
9629
|
-
const { includeSettings = false } = options;
|
|
9630
|
-
const modules = [];
|
|
9631
|
-
for (const mod of listModules()) {
|
|
9632
|
-
const gitInfo = getGitInfo(mod.path);
|
|
9633
|
-
modules.push({
|
|
9634
|
-
name: mod.name,
|
|
9635
|
-
source: gitInfo.remote || "local",
|
|
9636
|
-
version: mod.version,
|
|
9637
|
-
commit: gitInfo.commit,
|
|
9638
|
-
branch: gitInfo.branch
|
|
9639
|
-
});
|
|
9640
|
-
}
|
|
9641
|
-
const spaces = [];
|
|
9642
|
-
for (const space of listSpaces()) {
|
|
9643
|
-
const gitInfo = getGitInfo(space.path);
|
|
9644
|
-
spaces.push({
|
|
9645
|
-
name: space.name,
|
|
9646
|
-
number: space.number,
|
|
9647
|
-
type: space.type,
|
|
9648
|
-
source: gitInfo.remote,
|
|
9649
|
-
commit: gitInfo.commit
|
|
9650
|
-
});
|
|
9651
|
-
}
|
|
9652
|
-
const deps = checkDependencies();
|
|
9653
|
-
const dependencies = deps.filter((d) => d.installed && d.version).map((d) => ({
|
|
9654
|
-
name: d.name,
|
|
9655
|
-
version: d.version,
|
|
9656
|
-
required: d.required
|
|
9657
|
-
}));
|
|
9658
|
-
const snapshot = {
|
|
9659
|
-
version: "1.0",
|
|
9660
|
-
created: new Date().toISOString(),
|
|
9661
|
-
cliVersion: CLI_VERSION,
|
|
9662
|
-
platform: `${process.platform}-${process.arch}`,
|
|
9663
|
-
modules,
|
|
9664
|
-
spaces,
|
|
9665
|
-
dependencies
|
|
9666
|
-
};
|
|
9667
|
-
if (includeSettings) {
|
|
9668
|
-
const settingsPath = join8(DATA_DIR6, ".datacore", "settings.yaml");
|
|
9669
|
-
if (existsSync8(settingsPath)) {
|
|
9670
|
-
try {
|
|
9671
|
-
const content = readFileSync4(settingsPath, "utf-8");
|
|
9672
|
-
snapshot.settings = import_yaml2.parse(content);
|
|
9673
|
-
} catch {}
|
|
9674
|
-
}
|
|
9675
|
-
}
|
|
9676
|
-
return snapshot;
|
|
9677
|
-
}
|
|
9678
|
-
function saveSnapshot(snapshot, path) {
|
|
9679
|
-
const lockPath = path || LOCK_FILE;
|
|
9680
|
-
const content = import_yaml2.stringify(snapshot, {
|
|
9681
|
-
lineWidth: 0
|
|
9682
|
-
});
|
|
9683
|
-
writeFileSync5(lockPath, content);
|
|
9684
|
-
return lockPath;
|
|
9685
|
-
}
|
|
9686
|
-
function loadSnapshot(path) {
|
|
9687
|
-
const lockPath = path || LOCK_FILE;
|
|
9688
|
-
if (!existsSync8(lockPath)) {
|
|
9689
|
-
return null;
|
|
9690
|
-
}
|
|
9691
|
-
try {
|
|
9692
|
-
const content = readFileSync4(lockPath, "utf-8");
|
|
9693
|
-
return import_yaml2.parse(content);
|
|
9694
|
-
} catch {
|
|
9695
|
-
return null;
|
|
9696
|
-
}
|
|
9697
|
-
}
|
|
9698
|
-
function diffSnapshot(snapshot) {
|
|
9699
|
-
const current = createSnapshot();
|
|
9700
|
-
const diff = {
|
|
9701
|
-
modules: { added: [], removed: [], changed: [] },
|
|
9702
|
-
spaces: { added: [], removed: [] },
|
|
9703
|
-
dependencies: { changed: [] }
|
|
9704
|
-
};
|
|
9705
|
-
const currentModules = new Map(current.modules.map((m) => [m.name, m]));
|
|
9706
|
-
const snapshotModules = new Map(snapshot.modules.map((m) => [m.name, m]));
|
|
9707
|
-
for (const [name, mod] of currentModules) {
|
|
9708
|
-
if (!snapshotModules.has(name)) {
|
|
9709
|
-
diff.modules.added.push(name);
|
|
9710
|
-
} else {
|
|
9711
|
-
const expected = snapshotModules.get(name);
|
|
9712
|
-
if (expected.commit && mod.commit && expected.commit !== mod.commit) {
|
|
9713
|
-
diff.modules.changed.push({
|
|
9714
|
-
name,
|
|
9715
|
-
from: expected.commit.slice(0, 7),
|
|
9716
|
-
to: mod.commit.slice(0, 7)
|
|
9717
|
-
});
|
|
9718
|
-
}
|
|
9719
|
-
}
|
|
9720
|
-
}
|
|
9721
|
-
for (const name of snapshotModules.keys()) {
|
|
9722
|
-
if (!currentModules.has(name)) {
|
|
9723
|
-
diff.modules.removed.push(name);
|
|
9724
|
-
}
|
|
9725
|
-
}
|
|
9726
|
-
const currentSpaces = new Set(current.spaces.map((s) => s.name));
|
|
9727
|
-
const snapshotSpaces = new Set(snapshot.spaces.map((s) => s.name));
|
|
9728
|
-
for (const name of currentSpaces) {
|
|
9729
|
-
if (!snapshotSpaces.has(name)) {
|
|
9730
|
-
diff.spaces.added.push(name);
|
|
9731
|
-
}
|
|
9732
|
-
}
|
|
9733
|
-
for (const name of snapshotSpaces) {
|
|
9734
|
-
if (!currentSpaces.has(name)) {
|
|
9735
|
-
diff.spaces.removed.push(name);
|
|
9736
|
-
}
|
|
9737
|
-
}
|
|
9738
|
-
const currentDeps = new Map(current.dependencies.map((d) => [d.name, d.version]));
|
|
9739
|
-
for (const dep of snapshot.dependencies) {
|
|
9740
|
-
const actualVersion = currentDeps.get(dep.name);
|
|
9741
|
-
if (actualVersion && actualVersion !== dep.version) {
|
|
9742
|
-
diff.dependencies.changed.push({
|
|
9743
|
-
name: dep.name,
|
|
9744
|
-
expected: dep.version,
|
|
9745
|
-
actual: actualVersion
|
|
9746
|
-
});
|
|
9747
|
-
}
|
|
9748
|
-
}
|
|
9749
|
-
return diff;
|
|
9750
|
-
}
|
|
9751
|
-
function restoreFromSnapshot(snapshot, options = {}) {
|
|
9752
|
-
const { modules = true, spaces = true, dryRun = false } = options;
|
|
9753
|
-
const result = {
|
|
9754
|
-
modulesInstalled: [],
|
|
9755
|
-
modulesFailed: [],
|
|
9756
|
-
spacesCreated: [],
|
|
9757
|
-
warnings: []
|
|
9758
|
-
};
|
|
9759
|
-
if (modules) {
|
|
9760
|
-
const currentModules = new Set(listModules().map((m) => m.name));
|
|
9761
|
-
for (const mod of snapshot.modules) {
|
|
9762
|
-
if (currentModules.has(mod.name)) {
|
|
9763
|
-
continue;
|
|
9764
|
-
}
|
|
9765
|
-
if (mod.source === "local") {
|
|
9766
|
-
result.warnings.push(`Module ${mod.name} was local, cannot restore`);
|
|
9767
|
-
continue;
|
|
9768
|
-
}
|
|
9769
|
-
if (dryRun) {
|
|
9770
|
-
result.modulesInstalled.push(mod.name);
|
|
9771
|
-
continue;
|
|
9772
|
-
}
|
|
9773
|
-
try {
|
|
9774
|
-
const modulesDir = join8(DATA_DIR6, ".datacore", "modules");
|
|
9775
|
-
if (!existsSync8(modulesDir)) {
|
|
9776
|
-
mkdirSync5(modulesDir, { recursive: true });
|
|
9777
|
-
}
|
|
9778
|
-
const modulePath = join8(modulesDir, mod.name);
|
|
9779
|
-
execSync5(`git clone ${mod.source} "${modulePath}"`, { stdio: "pipe" });
|
|
9780
|
-
if (mod.commit) {
|
|
9781
|
-
execSync5(`git checkout ${mod.commit}`, { cwd: modulePath, stdio: "pipe" });
|
|
9782
|
-
} else if (mod.branch) {
|
|
9783
|
-
execSync5(`git checkout ${mod.branch}`, { cwd: modulePath, stdio: "pipe" });
|
|
9784
|
-
}
|
|
9785
|
-
result.modulesInstalled.push(mod.name);
|
|
9786
|
-
} catch (err) {
|
|
9787
|
-
result.modulesFailed.push({
|
|
9788
|
-
name: mod.name,
|
|
9789
|
-
error: err.message
|
|
9790
|
-
});
|
|
9791
|
-
}
|
|
9792
|
-
}
|
|
9793
|
-
}
|
|
9794
|
-
if (spaces) {
|
|
9795
|
-
const currentSpaces = new Set(listSpaces().map((s) => s.name));
|
|
9796
|
-
for (const space of snapshot.spaces) {
|
|
9797
|
-
if (currentSpaces.has(space.name)) {
|
|
9798
|
-
continue;
|
|
9799
|
-
}
|
|
9800
|
-
if (dryRun) {
|
|
9801
|
-
result.spacesCreated.push(space.name);
|
|
9802
|
-
continue;
|
|
9803
|
-
}
|
|
9804
|
-
if (space.source) {
|
|
9805
|
-
try {
|
|
9806
|
-
const spacePath = join8(DATA_DIR6, space.name);
|
|
9807
|
-
execSync5(`git clone ${space.source} "${spacePath}"`, { stdio: "pipe" });
|
|
9808
|
-
if (space.commit) {
|
|
9809
|
-
execSync5(`git checkout ${space.commit}`, { cwd: spacePath, stdio: "pipe" });
|
|
9810
|
-
}
|
|
9811
|
-
result.spacesCreated.push(space.name);
|
|
9812
|
-
} catch (err) {
|
|
9813
|
-
result.warnings.push(`Could not clone space ${space.name}: ${err.message}`);
|
|
9814
|
-
}
|
|
9815
|
-
} else {
|
|
9816
|
-
result.warnings.push(`Space ${space.name} has no git source, skipping`);
|
|
9817
|
-
}
|
|
9818
|
-
}
|
|
9819
|
-
}
|
|
9820
|
-
return result;
|
|
9821
|
-
}
|
|
9822
|
-
|
|
9823
10926
|
// src/index.ts
|
|
9824
|
-
var VERSION = "1.0.
|
|
10927
|
+
var VERSION = "1.0.7";
|
|
9825
10928
|
var args = process.argv.slice(2);
|
|
9826
10929
|
var parsed = parseArgs(args);
|
|
9827
10930
|
async function handleMeta(command, cmdArgs, flags, format) {
|
|
@@ -9832,17 +10935,20 @@ async function handleMeta(command, cmdArgs, flags, format) {
|
|
|
9832
10935
|
case "init": {
|
|
9833
10936
|
if (isInitialized() && !flags.force) {
|
|
9834
10937
|
if (format === "json") {
|
|
9835
|
-
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);
|
|
9836
10939
|
} else {
|
|
9837
|
-
info("Datacore already initialized");
|
|
9838
|
-
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");
|
|
9839
10944
|
}
|
|
9840
10945
|
break;
|
|
9841
10946
|
}
|
|
9842
10947
|
const result = await initDatacore({
|
|
9843
10948
|
nonInteractive: flags.yes === true,
|
|
9844
10949
|
skipChecks: flags["skip-checks"] === true,
|
|
9845
|
-
stream: format === "human"
|
|
10950
|
+
stream: format === "human",
|
|
10951
|
+
verbose: flags.verbose === true
|
|
9846
10952
|
});
|
|
9847
10953
|
if (format === "json") {
|
|
9848
10954
|
output(result, format);
|
|
@@ -9895,7 +11001,7 @@ async function handleMeta(command, cmdArgs, flags, format) {
|
|
|
9895
11001
|
for (const dep of result.dependencies) {
|
|
9896
11002
|
const status = dep.installed ? "✓" : "✗";
|
|
9897
11003
|
const version = dep.version ? ` (${dep.version})` : "";
|
|
9898
|
-
const required = dep.required ? "" : " (
|
|
11004
|
+
const required = dep.required ? "" : " (recommended)";
|
|
9899
11005
|
console.log(` ${status} ${dep.name}${version}${required}`);
|
|
9900
11006
|
if (!dep.installed && dep.installCommand) {
|
|
9901
11007
|
console.log(` Install: ${dep.installCommand}`);
|
|
@@ -10252,8 +11358,8 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10252
11358
|
throw new CLIError("ERR_INVALID_ARGUMENT", "Missing task description", 'Usage: datacore nightshift queue "Research topic X"');
|
|
10253
11359
|
}
|
|
10254
11360
|
const { existsSync: existsSync10, appendFileSync } = await import("fs");
|
|
10255
|
-
const { join:
|
|
10256
|
-
const inboxPath =
|
|
11361
|
+
const { join: join11 } = await import("path");
|
|
11362
|
+
const inboxPath = join11(process.env.HOME || "~", "Data", "0-personal", "org", "inbox.org");
|
|
10257
11363
|
if (!existsSync10(inboxPath)) {
|
|
10258
11364
|
throw new CLIError("ERR_NOT_FOUND", "inbox.org not found. Run datacore init first.");
|
|
10259
11365
|
}
|
|
@@ -10277,13 +11383,13 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10277
11383
|
break;
|
|
10278
11384
|
}
|
|
10279
11385
|
case "cron": {
|
|
10280
|
-
const { execSync:
|
|
11386
|
+
const { execSync: execSync5, exec: execAsync } = await import("child_process");
|
|
10281
11387
|
switch (action) {
|
|
10282
11388
|
case "install": {
|
|
10283
11389
|
info("Installing cron jobs...");
|
|
10284
11390
|
let currentCron = "";
|
|
10285
11391
|
try {
|
|
10286
|
-
currentCron =
|
|
11392
|
+
currentCron = execSync5("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
10287
11393
|
} catch {}
|
|
10288
11394
|
if (currentCron.includes("# Datacore")) {
|
|
10289
11395
|
if (!flags.force) {
|
|
@@ -10306,7 +11412,7 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10306
11412
|
`;
|
|
10307
11413
|
const newCron = currentCron.trim() + `
|
|
10308
11414
|
` + datacoreCron;
|
|
10309
|
-
|
|
11415
|
+
execSync5(`echo "${newCron}" | crontab -`, { stdio: "pipe" });
|
|
10310
11416
|
success("Cron jobs installed");
|
|
10311
11417
|
info(" 8:00 AM weekdays - Daily briefing");
|
|
10312
11418
|
info(" 6:00 PM weekdays - Evening wrap-up");
|
|
@@ -10316,7 +11422,7 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10316
11422
|
case "status": {
|
|
10317
11423
|
let crontab = "";
|
|
10318
11424
|
try {
|
|
10319
|
-
crontab =
|
|
11425
|
+
crontab = execSync5("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
10320
11426
|
} catch {}
|
|
10321
11427
|
const datacoreJobs = crontab.split(`
|
|
10322
11428
|
`).filter((line) => line.includes("datacore") && !line.startsWith("#"));
|
|
@@ -10339,7 +11445,7 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10339
11445
|
info("Removing cron jobs...");
|
|
10340
11446
|
let currentCron = "";
|
|
10341
11447
|
try {
|
|
10342
|
-
currentCron =
|
|
11448
|
+
currentCron = execSync5("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
10343
11449
|
} catch {
|
|
10344
11450
|
info("No cron jobs to remove");
|
|
10345
11451
|
break;
|
|
@@ -10348,9 +11454,9 @@ async function handleResource(resource, action, cmdArgs, flags, format) {
|
|
|
10348
11454
|
`).filter((line) => !line.includes("# Datacore") && !line.includes("datacore")).join(`
|
|
10349
11455
|
`).trim();
|
|
10350
11456
|
if (newCron) {
|
|
10351
|
-
|
|
11457
|
+
execSync5(`echo "${newCron}" | crontab -`, { stdio: "pipe" });
|
|
10352
11458
|
} else {
|
|
10353
|
-
|
|
11459
|
+
execSync5("crontab -r", { stdio: "pipe" });
|
|
10354
11460
|
}
|
|
10355
11461
|
success("Cron jobs removed");
|
|
10356
11462
|
break;
|