kodingo-cli 1.0.23 → 1.0.25
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/cli.js +21 -0
- package/dist/commands/context.js +105 -0
- package/dist/commands/guardrails.js +66 -0
- package/dist/commands/init.js +16 -1
- package/dist/commands/scan-repo.js +9 -3
- package/dist/commands/session-brief.js +114 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,9 @@ const init_1 = require("./commands/init");
|
|
|
18
18
|
const install_hook_1 = require("./commands/install-hook");
|
|
19
19
|
const update_1 = require("./commands/update");
|
|
20
20
|
const sync_1 = require("./commands/sync");
|
|
21
|
+
const context_1 = require("./commands/context");
|
|
22
|
+
const guardrails_1 = require("./commands/guardrails");
|
|
23
|
+
const session_brief_1 = require("./commands/session-brief");
|
|
21
24
|
const aging_1 = require("./commands/aging");
|
|
22
25
|
const program = new commander_1.Command();
|
|
23
26
|
program.name("kodingo").description("Kodingo CLI").version("1.0.4");
|
|
@@ -226,4 +229,22 @@ program
|
|
|
226
229
|
payload.repo = String(options.repo);
|
|
227
230
|
await (0, scan_repo_1.scanRepoCommand)(payload);
|
|
228
231
|
});
|
|
232
|
+
program
|
|
233
|
+
.command("context")
|
|
234
|
+
.description("Get affirmed memories for a file — designed for AI agents")
|
|
235
|
+
.requiredOption("-f, --file <path>", "File path to get context for")
|
|
236
|
+
.option("--prompt", "Output as prompt-ready string instead of JSON")
|
|
237
|
+
.option("--limit <n>", "Max memories to return", "20")
|
|
238
|
+
.action((opts) => (0, context_1.contextCommand)(opts.file, { prompt: opts.prompt, limit: parseInt(opts.limit) }));
|
|
239
|
+
program
|
|
240
|
+
.command("guardrails")
|
|
241
|
+
.description("Get project guardrails — rules and rejected patterns for AI agents")
|
|
242
|
+
.option("--prompt", "Output as prompt-ready string instead of JSON")
|
|
243
|
+
.action((opts) => (0, guardrails_1.guardrailsCommand)({ prompt: opts.prompt }));
|
|
244
|
+
program
|
|
245
|
+
.command("session-brief")
|
|
246
|
+
.description("Get a compact project intelligence brief — call at the start of an AI coding session")
|
|
247
|
+
.option("--prompt", "Output as prompt-ready string instead of JSON")
|
|
248
|
+
.option("--limit <n>", "Max memories to include", "30")
|
|
249
|
+
.action((opts) => (0, session_brief_1.sessionBriefCommand)({ prompt: opts.prompt, limit: parseInt(opts.limit) }));
|
|
229
250
|
program.parse(process.argv);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.contextCommand = contextCommand;
|
|
37
|
+
/**
|
|
38
|
+
* kodingo context --file <path>
|
|
39
|
+
*
|
|
40
|
+
* Returns all affirmed memories relevant to a file.
|
|
41
|
+
* Designed for AI agents — call before touching a file.
|
|
42
|
+
* Output: JSON by default, prompt-ready string with --prompt flag.
|
|
43
|
+
*/
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
46
|
+
const API_BASE_DEFAULT = "https://kodingo-api.onrender.com";
|
|
47
|
+
function normalizePath(p) {
|
|
48
|
+
return p.replace(/\\\\/g, "/").toLowerCase();
|
|
49
|
+
}
|
|
50
|
+
function getCredentials() {
|
|
51
|
+
const config = (0, persistence_config_1.readConfig)();
|
|
52
|
+
const cwd = normalizePath(process.cwd());
|
|
53
|
+
const ws = (0, persistence_config_1.readWorkspaceConfig)(cwd);
|
|
54
|
+
const token = ws?.token ?? config.token ?? "";
|
|
55
|
+
const apiUrl = ws?.apiUrl ?? config.apiUrl ?? API_BASE_DEFAULT;
|
|
56
|
+
return { token, apiUrl };
|
|
57
|
+
}
|
|
58
|
+
async function contextCommand(filePath, options) {
|
|
59
|
+
const { token, apiUrl } = getCredentials();
|
|
60
|
+
if (!token) {
|
|
61
|
+
console.error("No token found. Run `kodingo init` first.");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const limit = options.limit ?? 20;
|
|
65
|
+
const repoName = path.basename(process.cwd());
|
|
66
|
+
const relFile = path.relative(process.cwd(), path.resolve(filePath));
|
|
67
|
+
try {
|
|
68
|
+
// Fetch memories for this file (by repo path)
|
|
69
|
+
const res = await fetch(`${apiUrl}/memory?repo=${encodeURIComponent(relFile)}&status=affirmed&limit=${limit}`, { headers: { "X-Kodingo-Token": token } });
|
|
70
|
+
if (!res.ok) {
|
|
71
|
+
console.error(`API error: ${res.status}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
const data = await res.json();
|
|
75
|
+
const memories = data.memories ?? [];
|
|
76
|
+
if (!options.prompt) {
|
|
77
|
+
console.log(JSON.stringify({ file: relFile, repo: repoName, count: memories.length, memories }, null, 2));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Prompt-ready format
|
|
81
|
+
if (!memories.length) {
|
|
82
|
+
console.log(`No affirmed memories found for ${relFile}.`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const lines = [
|
|
86
|
+
`# Kortex context for ${relFile}`,
|
|
87
|
+
`# ${memories.length} affirmed decision${memories.length !== 1 ? "s" : ""} your team has made about this file:`,
|
|
88
|
+
"",
|
|
89
|
+
];
|
|
90
|
+
for (const m of memories) {
|
|
91
|
+
lines.push(`## ${m.title ?? "Decision"}`);
|
|
92
|
+
lines.push(m.content);
|
|
93
|
+
if (m.symbol)
|
|
94
|
+
lines.push(`Symbol: ${m.symbol}`);
|
|
95
|
+
lines.push(`Confidence: ${(m.confidence * 100).toFixed(0)}%`);
|
|
96
|
+
lines.push("");
|
|
97
|
+
}
|
|
98
|
+
lines.push("# End of Kortex context. Respect these decisions in your changes.");
|
|
99
|
+
console.log(lines.join("\n"));
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
console.error("Failed to fetch context:", err.message);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.guardrailsCommand = guardrailsCommand;
|
|
4
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
5
|
+
const API_BASE_DEFAULT = "https://kodingo-api.onrender.com";
|
|
6
|
+
function normalizePath(p) {
|
|
7
|
+
return p.replace(/\\\\/g, "/").toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
function getCredentials() {
|
|
10
|
+
const config = (0, persistence_config_1.readConfig)();
|
|
11
|
+
const cwd = normalizePath(process.cwd());
|
|
12
|
+
const ws = (0, persistence_config_1.readWorkspaceConfig)(cwd);
|
|
13
|
+
const token = ws?.token ?? config.token ?? "";
|
|
14
|
+
const apiUrl = ws?.apiUrl ?? config.apiUrl ?? API_BASE_DEFAULT;
|
|
15
|
+
return { token, apiUrl };
|
|
16
|
+
}
|
|
17
|
+
async function guardrailsCommand(options) {
|
|
18
|
+
const { token, apiUrl } = getCredentials();
|
|
19
|
+
if (!token) {
|
|
20
|
+
console.error("No token found. Run `kodingo init` first.");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
// Fetch denied memories (things explicitly ruled out)
|
|
25
|
+
const [deniedRes, affiredRes] = await Promise.all([
|
|
26
|
+
fetch(`${apiUrl}/memory?status=denied&limit=50`, { headers: { "X-Kodingo-Token": token } }),
|
|
27
|
+
fetch(`${apiUrl}/memory?status=affirmed&limit=50`, { headers: { "X-Kodingo-Token": token } }),
|
|
28
|
+
]);
|
|
29
|
+
const denied = deniedRes.ok ? (await deniedRes.json()).memories ?? [] : [];
|
|
30
|
+
const affirmed = affiredRes.ok ? (await affiredRes.json()).memories ?? [] : [];
|
|
31
|
+
// High confidence affirmed = guardrails (confidence >= 0.7)
|
|
32
|
+
const rules = affirmed.filter((m) => m.confidence >= 0.7);
|
|
33
|
+
if (!options.prompt) {
|
|
34
|
+
console.log(JSON.stringify({ rules: rules.length, denied: denied.length, rules_list: rules, denied_list: denied }, null, 2));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const lines = ["# Kortex guardrails — decisions your team has enforced", ""];
|
|
38
|
+
if (rules.length) {
|
|
39
|
+
lines.push("## Rules to follow:");
|
|
40
|
+
for (const m of rules) {
|
|
41
|
+
lines.push(`- ${m.title ?? m.content.slice(0, 100)}`);
|
|
42
|
+
if (m.symbol)
|
|
43
|
+
lines.push(` (applies to: ${m.symbol})`);
|
|
44
|
+
}
|
|
45
|
+
lines.push("");
|
|
46
|
+
}
|
|
47
|
+
if (denied.length) {
|
|
48
|
+
lines.push("## Patterns explicitly rejected by your team (do NOT use):");
|
|
49
|
+
for (const m of denied) {
|
|
50
|
+
lines.push(`- ${m.title ?? m.content.slice(0, 100)}`);
|
|
51
|
+
if (m.symbol)
|
|
52
|
+
lines.push(` (rejected for: ${m.symbol})`);
|
|
53
|
+
}
|
|
54
|
+
lines.push("");
|
|
55
|
+
}
|
|
56
|
+
if (!rules.length && !denied.length) {
|
|
57
|
+
lines.push("No guardrails defined yet. Affirm and deny memories to build them.");
|
|
58
|
+
}
|
|
59
|
+
lines.push("# End of Kortex guardrails.");
|
|
60
|
+
console.log(lines.join("\n"));
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.error("Failed to fetch guardrails:", err.message);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -56,6 +56,20 @@ const API_BASE = "https://kodingo-api.onrender.com";
|
|
|
56
56
|
const APP_URL = "https://kodingo.xyz";
|
|
57
57
|
const CONFIG_DIR = path.join(os.homedir(), ".kodingo");
|
|
58
58
|
const AUTH_TOKEN_PATH = path.join(CONFIG_DIR, "auth.json");
|
|
59
|
+
// ── Machine ID ────────────────────────────────────────────────────────────────
|
|
60
|
+
function generateMachineId() {
|
|
61
|
+
const raw = `${os.hostname()}::${os.userInfo().username}::${process.platform}`;
|
|
62
|
+
return require("crypto").createHash("sha256").update(raw).digest("hex").slice(0, 32);
|
|
63
|
+
}
|
|
64
|
+
function getMachineId() {
|
|
65
|
+
const config = (0, persistence_config_1.readConfig)();
|
|
66
|
+
if (config.machineId)
|
|
67
|
+
return config.machineId;
|
|
68
|
+
const machineId = generateMachineId();
|
|
69
|
+
const existing = (0, persistence_config_1.readConfig)();
|
|
70
|
+
(0, persistence_config_1.writeConfig)({ ...existing, machineId });
|
|
71
|
+
return machineId;
|
|
72
|
+
}
|
|
59
73
|
// ── Auth token storage ────────────────────────────────────────────────────────
|
|
60
74
|
function readAuthToken() {
|
|
61
75
|
try {
|
|
@@ -255,7 +269,8 @@ function registerInitCommand(program) {
|
|
|
255
269
|
const apiUrl = API_BASE;
|
|
256
270
|
// preserve existing workspaces, never overwrite global token
|
|
257
271
|
const existingConfig = (0, persistence_config_1.readConfig)();
|
|
258
|
-
|
|
272
|
+
const machineId = getMachineId();
|
|
273
|
+
(0, persistence_config_1.writeConfig)({ ...existingConfig, mode: "cloud", apiUrl, machineId });
|
|
259
274
|
(0, persistence_config_1.writeWorkspaceConfig)(repoRoot, { mode: "cloud", apiUrl, token: selectedProject.token });
|
|
260
275
|
ensureGitignoreEntry(repoRoot, ".kortex/");
|
|
261
276
|
console.log(`✔ Connected. Token saved to ${persistence_config_1.CONFIG_PATH}`);
|
|
@@ -38,7 +38,7 @@ const IGNORED_DIRS = [
|
|
|
38
38
|
"node_modules", ".git", ".kortex", "dist", "build",
|
|
39
39
|
"out", ".next", "coverage", "__pycache__", "vendor",
|
|
40
40
|
];
|
|
41
|
-
const MAX_SYMBOLS =
|
|
41
|
+
const MAX_SYMBOLS = 200;
|
|
42
42
|
const BATCH_SIZE = 5;
|
|
43
43
|
const BATCH_DELAY_MS = 1200;
|
|
44
44
|
function findRepoRoot(startPath) {
|
|
@@ -107,8 +107,12 @@ async function fetchExistingSymbols(token, repo) {
|
|
|
107
107
|
break;
|
|
108
108
|
const data = await res.json();
|
|
109
109
|
for (const m of data.memories ?? []) {
|
|
110
|
-
if (m.symbol)
|
|
110
|
+
if (m.symbol) {
|
|
111
|
+
// Store both symbol-only and symbol::repo keys for accurate matching
|
|
111
112
|
known.add(m.symbol);
|
|
113
|
+
if (m.repo)
|
|
114
|
+
known.add(`${m.symbol}::${m.repo}`);
|
|
115
|
+
}
|
|
112
116
|
}
|
|
113
117
|
offset += limit;
|
|
114
118
|
if (offset >= (data.total ?? 0))
|
|
@@ -195,7 +199,9 @@ async function scanRepoCommand(options = {}) {
|
|
|
195
199
|
if (seen.has(key))
|
|
196
200
|
continue;
|
|
197
201
|
seen.add(key);
|
|
198
|
-
|
|
202
|
+
const symRepoKey = `${sym.name}::${path_1.default.basename(sym.file)}`;
|
|
203
|
+
const hasMemory = existingSymbols.has(symRepoKey) || existingSymbols.has(sym.name);
|
|
204
|
+
if (!hasMemory)
|
|
199
205
|
toCapture.push(sym);
|
|
200
206
|
}
|
|
201
207
|
if (toCapture.length === 0) {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.sessionBriefCommand = sessionBriefCommand;
|
|
37
|
+
/**
|
|
38
|
+
* kodingo session-brief
|
|
39
|
+
*
|
|
40
|
+
* Returns a compact summary of the most important decisions across the project.
|
|
41
|
+
* Designed for AI agents — call once at the start of a new coding session.
|
|
42
|
+
* Output: JSON by default, prompt-ready string with --prompt flag.
|
|
43
|
+
*/
|
|
44
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const API_BASE_DEFAULT = "https://kodingo-api.onrender.com";
|
|
47
|
+
function normalizePath(p) {
|
|
48
|
+
return p.replace(/\\\\/g, "/").toLowerCase();
|
|
49
|
+
}
|
|
50
|
+
function getCredentials() {
|
|
51
|
+
const config = (0, persistence_config_1.readConfig)();
|
|
52
|
+
const cwd = normalizePath(process.cwd());
|
|
53
|
+
const ws = (0, persistence_config_1.readWorkspaceConfig)(cwd);
|
|
54
|
+
const token = ws?.token ?? config.token ?? "";
|
|
55
|
+
const apiUrl = ws?.apiUrl ?? config.apiUrl ?? API_BASE_DEFAULT;
|
|
56
|
+
return { token, apiUrl };
|
|
57
|
+
}
|
|
58
|
+
async function sessionBriefCommand(options) {
|
|
59
|
+
const { token, apiUrl } = getCredentials();
|
|
60
|
+
if (!token) {
|
|
61
|
+
console.error("No token found. Run `kodingo init` first.");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const limit = options.limit ?? 30;
|
|
65
|
+
const repoName = path.basename(process.cwd());
|
|
66
|
+
try {
|
|
67
|
+
// Top affirmed memories sorted by confidence
|
|
68
|
+
const res = await fetch(`${apiUrl}/memory?status=affirmed&limit=${limit}`, { headers: { "X-Kodingo-Token": token } });
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
console.error(`API error: ${res.status}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const data = await res.json();
|
|
74
|
+
const memories = (data.memories ?? []).sort((a, b) => b.confidence - a.confidence);
|
|
75
|
+
if (!options.prompt) {
|
|
76
|
+
console.log(JSON.stringify({
|
|
77
|
+
repo: repoName,
|
|
78
|
+
total_affirmed: data.total,
|
|
79
|
+
showing: memories.length,
|
|
80
|
+
memories,
|
|
81
|
+
}, null, 2));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const lines = [
|
|
85
|
+
`# Kortex session brief for ${repoName}`,
|
|
86
|
+
`# ${data.total} total affirmed decisions. Showing top ${memories.length} by confidence.`,
|
|
87
|
+
`# Read these before writing any code.`,
|
|
88
|
+
"",
|
|
89
|
+
];
|
|
90
|
+
// Group by type
|
|
91
|
+
const byType = {};
|
|
92
|
+
for (const m of memories) {
|
|
93
|
+
const t = m.type ?? "context";
|
|
94
|
+
if (!byType[t])
|
|
95
|
+
byType[t] = [];
|
|
96
|
+
byType[t].push(m);
|
|
97
|
+
}
|
|
98
|
+
for (const [type, items] of Object.entries(byType)) {
|
|
99
|
+
lines.push(`## ${type.charAt(0).toUpperCase() + type.slice(1)} decisions`);
|
|
100
|
+
for (const m of items) {
|
|
101
|
+
const conf = (m.confidence * 100).toFixed(0);
|
|
102
|
+
const symbol = m.symbol ? ` [${m.symbol}]` : "";
|
|
103
|
+
lines.push(`- ${m.title ?? m.content.slice(0, 100)}${symbol} (${conf}% confidence)`);
|
|
104
|
+
}
|
|
105
|
+
lines.push("");
|
|
106
|
+
}
|
|
107
|
+
lines.push("# End of Kortex session brief. These decisions represent your team\'s collective intelligence.");
|
|
108
|
+
console.log(lines.join("\n"));
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
console.error("Failed to fetch session brief:", err.message);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|