chapterhouse 0.5.0 → 0.5.2
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/api/server.js +31 -3
- package/dist/api/server.test.js +48 -5
- package/dist/cli.js +4 -2
- package/dist/config.js +75 -13
- package/dist/config.test.js +44 -0
- package/dist/copilot/orchestrator.js +17 -6
- package/dist/copilot/orchestrator.test.js +50 -3
- package/dist/copilot/router.js +43 -8
- package/dist/copilot/router.test.js +30 -18
- package/dist/copilot/system-message.js +3 -3
- package/dist/copilot/system-message.test.js +10 -0
- package/dist/copilot/tools.js +79 -12
- package/dist/copilot/tools.memory.test.js +94 -6
- package/dist/daemon.js +7 -2
- package/dist/integrations/team-push.js +8 -1
- package/dist/integrations/teams-notify.js +8 -1
- package/dist/memory/active-scope.test.js +7 -2
- package/dist/memory/eot.js +96 -8
- package/dist/memory/eot.test.js +186 -5
- package/dist/memory/hot-tier.test.js +14 -4
- package/dist/memory/housekeeping.js +73 -25
- package/dist/memory/housekeeping.test.js +115 -16
- package/dist/memory/inbox.test.js +178 -0
- package/dist/memory/scopes.test.js +0 -24
- package/dist/memory/tiering.test.js +323 -0
- package/dist/mode-context.js +28 -0
- package/dist/mode-context.test.js +42 -0
- package/dist/setup.js +152 -95
- package/dist/setup.test.js +122 -0
- package/dist/store/db.js +0 -18
- package/dist/wiki/team-sync.js +8 -1
- package/package.json +1 -1
- package/web/dist/assets/{index-BfHqP3-C.js → index-CPaILy2j.js} +69 -69
- package/web/dist/assets/index-CPaILy2j.js.map +1 -0
- package/web/dist/assets/index-Cs7AGeaL.css +10 -0
- package/web/dist/index.html +2 -2
- package/agents/bellonda.agent.md +0 -11
- package/agents/hwi-noree.agent.md +0 -12
- package/web/dist/assets/index-BfHqP3-C.js.map +0 -1
- package/web/dist/assets/index-_O6AoWOS.css +0 -10
package/dist/setup.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import * as readline from "readline";
|
|
2
|
-
import {
|
|
1
|
+
import * as readline from "node:readline";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { CopilotClient } from "@github/copilot-sdk";
|
|
4
|
-
import { ensureChapterhouseHome, ENV_PATH,
|
|
5
|
+
import { CHAPTERHOUSE_HOME, ensureChapterhouseHome, ENV_PATH, WIKI_DIR } from "./paths.js";
|
|
5
6
|
import { getExampleProjectPath } from "./home-path.js";
|
|
6
7
|
const BOLD = "\x1b[1m";
|
|
7
8
|
const DIM = "\x1b[2m";
|
|
@@ -21,11 +22,11 @@ async function fetchModels() {
|
|
|
21
22
|
await client.start();
|
|
22
23
|
const models = await client.listModels();
|
|
23
24
|
return models
|
|
24
|
-
.filter((
|
|
25
|
-
.map((
|
|
26
|
-
const mult =
|
|
25
|
+
.filter((model) => model.policy?.state === "enabled" && !model.name.includes("(Internal only)"))
|
|
26
|
+
.map((model) => {
|
|
27
|
+
const mult = model.billing?.multiplier;
|
|
27
28
|
const desc = mult === 0 || mult === undefined ? "Included with Copilot" : `Premium (${mult}x)`;
|
|
28
|
-
return { id:
|
|
29
|
+
return { id: model.id, label: model.name, desc };
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
32
|
catch {
|
|
@@ -35,7 +36,9 @@ async function fetchModels() {
|
|
|
35
36
|
try {
|
|
36
37
|
await client?.stop();
|
|
37
38
|
}
|
|
38
|
-
catch {
|
|
39
|
+
catch {
|
|
40
|
+
// best-effort cleanup
|
|
41
|
+
}
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
function ask(rl, question) {
|
|
@@ -43,107 +46,161 @@ function ask(rl, question) {
|
|
|
43
46
|
}
|
|
44
47
|
async function askPicker(rl, label, options, defaultId) {
|
|
45
48
|
console.log(`${BOLD}${label}${RESET}\n`);
|
|
46
|
-
const defaultIdx = Math.max(0, options.findIndex((
|
|
47
|
-
for (let
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
console.log(`
|
|
49
|
+
const defaultIdx = Math.max(0, options.findIndex((option) => option.id === defaultId));
|
|
50
|
+
for (let index = 0; index < options.length; index++) {
|
|
51
|
+
const option = options[index];
|
|
52
|
+
const marker = index === defaultIdx ? `${GREEN}▸${RESET}` : " ";
|
|
53
|
+
const tag = index === defaultIdx ? ` ${DIM}(default)${RESET}` : "";
|
|
54
|
+
console.log(` ${marker} ${CYAN}${index + 1}${RESET} ${option.label}${tag}`);
|
|
55
|
+
console.log(` ${DIM}${option.desc}${RESET}`);
|
|
52
56
|
}
|
|
53
57
|
console.log();
|
|
54
58
|
const input = await ask(rl, ` Pick a number ${DIM}(1-${options.length}, Enter for default)${RESET}: `);
|
|
55
|
-
const num = parseInt(input.trim(), 10);
|
|
56
|
-
if (num >= 1 && num <= options.length)
|
|
59
|
+
const num = Number.parseInt(input.trim(), 10);
|
|
60
|
+
if (num >= 1 && num <= options.length) {
|
|
57
61
|
return options[num - 1].id;
|
|
62
|
+
}
|
|
58
63
|
return options[defaultIdx].id;
|
|
59
64
|
}
|
|
60
|
-
|
|
65
|
+
function readExistingEnv() {
|
|
66
|
+
const lines = existsSync(ENV_PATH)
|
|
67
|
+
? readFileSync(ENV_PATH, "utf-8").split("\n").filter((line) => line.length > 0)
|
|
68
|
+
: [];
|
|
69
|
+
const values = {};
|
|
70
|
+
for (const line of lines) {
|
|
71
|
+
const match = line.match(/^([A-Z_]+)=(.*)$/);
|
|
72
|
+
if (match) {
|
|
73
|
+
values[match[1]] = match[2];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { lines, values };
|
|
77
|
+
}
|
|
78
|
+
function resolveSetupMode(existing) {
|
|
79
|
+
const configured = process.env.CHAPTERHOUSE_MODE?.trim() || existing.CHAPTERHOUSE_MODE?.trim() || "personal";
|
|
80
|
+
return configured === "team" ? "team" : "personal";
|
|
81
|
+
}
|
|
82
|
+
function upsertEnvLines(lines, updates) {
|
|
83
|
+
const pending = new Map(Object.entries(updates)
|
|
84
|
+
.filter(([, value]) => value.trim().length > 0));
|
|
85
|
+
const nextLines = lines.map((line) => {
|
|
86
|
+
const match = line.match(/^([A-Z_]+)=(.*)$/);
|
|
87
|
+
if (!match) {
|
|
88
|
+
return line;
|
|
89
|
+
}
|
|
90
|
+
const key = match[1];
|
|
91
|
+
const replacement = pending.get(key);
|
|
92
|
+
if (replacement === undefined) {
|
|
93
|
+
return line;
|
|
94
|
+
}
|
|
95
|
+
pending.delete(key);
|
|
96
|
+
return `${key}=${replacement}`;
|
|
97
|
+
});
|
|
98
|
+
for (const [key, value] of pending) {
|
|
99
|
+
nextLines.push(`${key}=${value}`);
|
|
100
|
+
}
|
|
101
|
+
return nextLines;
|
|
102
|
+
}
|
|
103
|
+
function hasTokenEnv() {
|
|
104
|
+
return Boolean(process.env.GITHUB_TOKEN?.trim() || process.env.COPILOT_TOKEN?.trim());
|
|
105
|
+
}
|
|
106
|
+
function hasGhAuth() {
|
|
107
|
+
try {
|
|
108
|
+
execFileSync("gh", ["auth", "status"], { stdio: "ignore" });
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function showWikiLocation(rl) {
|
|
116
|
+
console.log(`${BOLD}Personal wiki location${RESET}`);
|
|
117
|
+
console.log(` ${CYAN}${WIKI_DIR}${RESET}`);
|
|
118
|
+
console.log(`${DIM} Chapterhouse stores your local wiki here.${RESET}\n`);
|
|
119
|
+
await ask(rl, `${DIM}Press Enter to continue...${RESET}`);
|
|
120
|
+
console.log();
|
|
121
|
+
}
|
|
122
|
+
export async function runSetup() {
|
|
61
123
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
62
|
-
|
|
124
|
+
try {
|
|
125
|
+
console.log(`
|
|
63
126
|
${BOLD}╔══════════════════════════════════════════╗
|
|
64
127
|
║ 🤖 Chapterhouse Setup ║
|
|
65
128
|
╚══════════════════════════════════════════╝${RESET}
|
|
66
129
|
`);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
existing[match[1]] = match[2];
|
|
130
|
+
console.log(`${DIM}Config directory: ${CHAPTERHOUSE_HOME}${RESET}\n`);
|
|
131
|
+
ensureChapterhouseHome();
|
|
132
|
+
const existing = readExistingEnv();
|
|
133
|
+
const mode = resolveSetupMode(existing.values);
|
|
134
|
+
console.log(`${BOLD}${mode === "personal" ? "Personal setup" : "Team setup"}${RESET}`);
|
|
135
|
+
if (mode === "personal") {
|
|
136
|
+
console.log("We'll get your GitHub/Copilot auth, preferred model, and personal wiki location dialed in.");
|
|
137
|
+
console.log(`${DIM}Team-only integrations stay out of the way in personal mode.${RESET}`);
|
|
76
138
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
models
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
lines.push(`API_PORT=${apiPort}`);
|
|
118
|
-
lines.push(`COPILOT_MODEL=${model}`);
|
|
119
|
-
writeFileSync(ENV_PATH, lines.join("\n") + "\n");
|
|
120
|
-
// ── Done ─────────────────────────────────────────────────
|
|
121
|
-
console.log(`
|
|
139
|
+
else {
|
|
140
|
+
console.log("We'll keep the shared team configuration intact and confirm your local defaults.");
|
|
141
|
+
}
|
|
142
|
+
console.log();
|
|
143
|
+
await ask(rl, `${DIM}Press Enter to continue...${RESET}`);
|
|
144
|
+
console.log();
|
|
145
|
+
if (mode === "personal") {
|
|
146
|
+
if (!hasTokenEnv() && !hasGhAuth()) {
|
|
147
|
+
console.log(`${YELLOW}GitHub authentication is required before setup can continue.${RESET}`);
|
|
148
|
+
console.log("Run `gh auth login` to authenticate with GitHub, then re-run setup.");
|
|
149
|
+
console.log(`${DIM}If you already manage credentials via environment, set GITHUB_TOKEN or COPILOT_TOKEN before running setup.${RESET}`);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
console.log();
|
|
153
|
+
await showWikiLocation(rl);
|
|
154
|
+
}
|
|
155
|
+
console.log(`\n${BOLD}━━━ Default Model ━━━${RESET}\n`);
|
|
156
|
+
console.log(`${DIM}Fetching available models from Copilot...${RESET}`);
|
|
157
|
+
let models = await fetchModels();
|
|
158
|
+
if (models.length === 0) {
|
|
159
|
+
console.log(`${YELLOW} Could not fetch models (Copilot CLI may not be authenticated yet).${RESET}`);
|
|
160
|
+
console.log(`${DIM} Showing a curated list — you can switch anytime after setup.${RESET}\n`);
|
|
161
|
+
models = FALLBACK_MODELS;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
console.log(`${GREEN} ✓ Found ${models.length} models${RESET}\n`);
|
|
165
|
+
}
|
|
166
|
+
console.log(`${DIM}You can switch models anytime in the web UI's Settings page.${RESET}\n`);
|
|
167
|
+
const currentModel = existing.values.COPILOT_MODEL || "claude-sonnet-4.6";
|
|
168
|
+
const model = await askPicker(rl, "Choose a default model:", models, currentModel);
|
|
169
|
+
const modelLabel = models.find((entry) => entry.id === model)?.label || model;
|
|
170
|
+
console.log(`\n${GREEN} ✓ Using ${modelLabel}${RESET}\n`);
|
|
171
|
+
const apiPort = existing.values.API_PORT || "7788";
|
|
172
|
+
const nextEnv = upsertEnvLines(existing.lines, {
|
|
173
|
+
API_PORT: apiPort,
|
|
174
|
+
CHAPTERHOUSE_MODE: mode,
|
|
175
|
+
COPILOT_MODEL: model,
|
|
176
|
+
});
|
|
177
|
+
writeFileSync(ENV_PATH, `${nextEnv.join("\n")}\n`);
|
|
178
|
+
console.log(`
|
|
122
179
|
${GREEN}${BOLD}✅ Chapterhouse is ready!${RESET}
|
|
123
180
|
${DIM}Config saved to ${ENV_PATH}${RESET}
|
|
124
|
-
|
|
125
|
-
${BOLD}Get started:${RESET}
|
|
126
|
-
|
|
127
|
-
${CYAN}1.${RESET} Make sure Copilot CLI is authenticated:
|
|
128
|
-
${BOLD}copilot login${RESET}
|
|
129
|
-
|
|
130
|
-
${CYAN}2.${RESET} Start Chapterhouse:
|
|
131
|
-
${BOLD}chapterhouse start${RESET}
|
|
132
|
-
|
|
133
|
-
${CYAN}3.${RESET} Open the web UI:
|
|
134
|
-
${BOLD}http://localhost:${apiPort}${RESET}
|
|
135
|
-
|
|
136
|
-
${BOLD}Things to try:${RESET}
|
|
137
|
-
|
|
138
|
-
${DIM}"Start working on the auth bug in ${getExampleProjectPath()}"${RESET}
|
|
139
|
-
${DIM}"What sessions are running?"${RESET}
|
|
140
|
-
${DIM}"Find me a skill for checking Gmail"${RESET}
|
|
141
|
-
${DIM}"Switch to gpt-4.1"${RESET}
|
|
142
181
|
`);
|
|
143
|
-
|
|
182
|
+
if (mode === "personal") {
|
|
183
|
+
console.log(`${BOLD}Your personal wiki lives at:${RESET}`);
|
|
184
|
+
console.log(` ${CYAN}${WIKI_DIR}${RESET}\n`);
|
|
185
|
+
}
|
|
186
|
+
console.log(`${BOLD}Get started:${RESET}
|
|
187
|
+
`);
|
|
188
|
+
console.log(` ${CYAN}1.${RESET} If needed, authenticate GitHub CLI:`);
|
|
189
|
+
console.log(` ${BOLD}gh auth login${RESET}\n`);
|
|
190
|
+
console.log(` ${CYAN}2.${RESET} Make sure Copilot CLI is authenticated:`);
|
|
191
|
+
console.log(` ${BOLD}copilot login${RESET}\n`);
|
|
192
|
+
console.log(` ${CYAN}3.${RESET} Start Chapterhouse:`);
|
|
193
|
+
console.log(` ${BOLD}chapterhouse start${RESET}\n`);
|
|
194
|
+
console.log(` ${CYAN}4.${RESET} Open the web UI:`);
|
|
195
|
+
console.log(` ${BOLD}http://localhost:${apiPort}${RESET}\n`);
|
|
196
|
+
console.log(`${BOLD}Things to try:${RESET}\n`);
|
|
197
|
+
console.log(` ${DIM}"Start working on the auth bug in ${getExampleProjectPath()}"${RESET}`);
|
|
198
|
+
console.log(` ${DIM}"What sessions are running?"${RESET}`);
|
|
199
|
+
console.log(` ${DIM}"Find me a skill for checking Gmail"${RESET}`);
|
|
200
|
+
console.log(` ${DIM}"Switch to gpt-4.1"${RESET}`);
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
rl.close();
|
|
204
|
+
}
|
|
144
205
|
}
|
|
145
|
-
main().catch((err) => {
|
|
146
|
-
console.error("Setup failed:", err);
|
|
147
|
-
process.exit(1);
|
|
148
|
-
});
|
|
149
206
|
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
async function runSetupScript(t, options = {}) {
|
|
4
|
+
const output = [];
|
|
5
|
+
const prompts = [];
|
|
6
|
+
let writtenConfig = "";
|
|
7
|
+
const answers = [...(options.answers ?? [])];
|
|
8
|
+
t.mock.module("node:readline", {
|
|
9
|
+
namedExports: {
|
|
10
|
+
createInterface: () => ({
|
|
11
|
+
question: (prompt, callback) => {
|
|
12
|
+
prompts.push(prompt);
|
|
13
|
+
callback(answers.shift() ?? "");
|
|
14
|
+
},
|
|
15
|
+
close: () => { },
|
|
16
|
+
}),
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
t.mock.module("fs", {
|
|
20
|
+
namedExports: {
|
|
21
|
+
existsSync: () => options.existingEnv !== undefined,
|
|
22
|
+
readFileSync: () => options.existingEnv ?? "",
|
|
23
|
+
writeFileSync: (_path, content) => {
|
|
24
|
+
writtenConfig = content;
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
t.mock.module("./paths.js", {
|
|
29
|
+
namedExports: {
|
|
30
|
+
ensureChapterhouseHome: () => { },
|
|
31
|
+
ENV_PATH: "/workspace/.chapterhouse/.env",
|
|
32
|
+
CHAPTERHOUSE_HOME: "/workspace/.chapterhouse",
|
|
33
|
+
WIKI_DIR: "/workspace/.chapterhouse/wiki",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
t.mock.module("./home-path.js", {
|
|
37
|
+
namedExports: {
|
|
38
|
+
getExampleProjectPath: () => "/workspace/example-project",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
t.mock.module("node:child_process", {
|
|
42
|
+
namedExports: {
|
|
43
|
+
execFileSync: () => {
|
|
44
|
+
if (options.ghAuthStatus === "unauthenticated") {
|
|
45
|
+
throw new Error("gh auth status failed");
|
|
46
|
+
}
|
|
47
|
+
return Buffer.from("github.com\n ✓ Logged in");
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
t.mock.module("@github/copilot-sdk", {
|
|
52
|
+
namedExports: {
|
|
53
|
+
CopilotClient: class {
|
|
54
|
+
async start() { }
|
|
55
|
+
async stop() { }
|
|
56
|
+
async listModels() {
|
|
57
|
+
return (options.modelIds ?? ["claude-sonnet-4.6"]).map((id) => ({
|
|
58
|
+
id,
|
|
59
|
+
name: id,
|
|
60
|
+
policy: { state: "enabled" },
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
t.mock.method(console, "log", (...args) => {
|
|
67
|
+
output.push(args.join(" "));
|
|
68
|
+
});
|
|
69
|
+
t.mock.method(console, "error", (...args) => {
|
|
70
|
+
output.push(args.join(" "));
|
|
71
|
+
});
|
|
72
|
+
const priorEnv = process.env;
|
|
73
|
+
process.env = {
|
|
74
|
+
...priorEnv,
|
|
75
|
+
...options.env,
|
|
76
|
+
};
|
|
77
|
+
try {
|
|
78
|
+
const setupModule = await import(new URL(`./setup.js?case=${Date.now()}-${Math.random()}`, import.meta.url).href);
|
|
79
|
+
const { runSetup } = setupModule;
|
|
80
|
+
assert.equal(typeof runSetup, "function", "setup.ts should export runSetup");
|
|
81
|
+
await runSetup?.();
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
process.env = priorEnv;
|
|
85
|
+
}
|
|
86
|
+
return { output, prompts, writtenConfig };
|
|
87
|
+
}
|
|
88
|
+
test("personal setup uses existing gh auth and never prompts for tokens", async (t) => {
|
|
89
|
+
const result = await runSetupScript(t, {
|
|
90
|
+
env: { CHAPTERHOUSE_MODE: "personal" },
|
|
91
|
+
answers: ["", "", "1"],
|
|
92
|
+
ghAuthStatus: "authenticated",
|
|
93
|
+
});
|
|
94
|
+
assert.doesNotMatch(result.prompts.join("\n"), /token/i);
|
|
95
|
+
assert.match(result.output.join("\n"), /wiki location/i);
|
|
96
|
+
assert.doesNotMatch(result.output.join("\n"), /ADO|Teams webhook|Entra/i);
|
|
97
|
+
assert.match(result.writtenConfig, /^CHAPTERHOUSE_MODE=personal$/m);
|
|
98
|
+
assert.match(result.writtenConfig, /^COPILOT_MODEL=claude-sonnet-4\.6$/m);
|
|
99
|
+
assert.doesNotMatch(result.writtenConfig, /^GITHUB_TOKEN=/m);
|
|
100
|
+
});
|
|
101
|
+
test("personal setup accepts env var tokens silently without prompting", async (t) => {
|
|
102
|
+
const result = await runSetupScript(t, {
|
|
103
|
+
env: { CHAPTERHOUSE_MODE: "personal", GITHUB_TOKEN: "ghp_env_token" },
|
|
104
|
+
answers: ["", "", "1"],
|
|
105
|
+
ghAuthStatus: "unauthenticated",
|
|
106
|
+
});
|
|
107
|
+
assert.doesNotMatch(result.prompts.join("\n"), /token/i);
|
|
108
|
+
assert.match(result.writtenConfig, /^CHAPTERHOUSE_MODE=personal$/m);
|
|
109
|
+
assert.match(result.writtenConfig, /^COPILOT_MODEL=claude-sonnet-4\.6$/m);
|
|
110
|
+
assert.doesNotMatch(result.writtenConfig, /^GITHUB_TOKEN=/m);
|
|
111
|
+
});
|
|
112
|
+
test("personal setup instructs users to run gh auth login when not authenticated", async (t) => {
|
|
113
|
+
const result = await runSetupScript(t, {
|
|
114
|
+
env: { CHAPTERHOUSE_MODE: "personal" },
|
|
115
|
+
answers: [""],
|
|
116
|
+
ghAuthStatus: "unauthenticated",
|
|
117
|
+
});
|
|
118
|
+
assert.doesNotMatch(result.prompts.join("\n"), /token/i);
|
|
119
|
+
assert.match(result.output.join("\n"), /Run `gh auth login` to authenticate with GitHub, then re-run setup/i);
|
|
120
|
+
assert.equal(result.writtenConfig, "");
|
|
121
|
+
});
|
|
122
|
+
//# sourceMappingURL=setup.test.js.map
|
package/dist/store/db.js
CHANGED
|
@@ -216,24 +216,6 @@ const MEMORY_SCOPE_SEEDS = [
|
|
|
216
216
|
description: "Chapterhouse codebase, conventions, decisions, gotchas",
|
|
217
217
|
keywords: ["chapterhouse", "this repo", "this project", "the daemon"],
|
|
218
218
|
},
|
|
219
|
-
{
|
|
220
|
-
slug: "infra",
|
|
221
|
-
title: "Infra",
|
|
222
|
-
description: "Infrastructure, hosting, deployment, CI/CD",
|
|
223
|
-
keywords: ["infra"],
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
slug: "team",
|
|
227
|
-
title: "Team",
|
|
228
|
-
description: "Team processes, rituals, OKRs",
|
|
229
|
-
keywords: ["team"],
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
slug: "brian",
|
|
233
|
-
title: "Brian",
|
|
234
|
-
description: "Brian's preferences, context, working style",
|
|
235
|
-
keywords: ["brian"],
|
|
236
|
-
},
|
|
237
219
|
];
|
|
238
220
|
const CHAPTERHOUSE_WIKI_INDEX_SOURCE = "wiki:pages/projects/chapterhouse/index.md";
|
|
239
221
|
const CHAPTERHOUSE_WIKI_HOT_TIER_REASON = "P6 PR1 wiki migration hot-tier candidate";
|
package/dist/wiki/team-sync.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { config } from "../config.js";
|
|
4
|
+
import { ModeContext } from "../mode-context.js";
|
|
4
5
|
import { WIKI_DIR } from "../paths.js";
|
|
5
6
|
import { assertPagePath, readPage, writePage, writeFileAtomic } from "./fs.js";
|
|
6
7
|
import { addToIndex, buildIndexEntryForPage } from "./index-manager.js";
|
|
@@ -18,6 +19,7 @@ export class TeamWikiSync {
|
|
|
18
19
|
fetchImpl;
|
|
19
20
|
warn;
|
|
20
21
|
now;
|
|
22
|
+
modeContext;
|
|
21
23
|
constructor(options = {}) {
|
|
22
24
|
this.teamChapterhouseUrl = (options.teamChapterhouseUrl ?? config.teamChapterhouseUrl).trim().replace(/\/+$/, "");
|
|
23
25
|
this.teamChapterhouseToken = (options.teamChapterhouseToken ?? config.teamChapterhouseToken).trim();
|
|
@@ -32,9 +34,14 @@ export class TeamWikiSync {
|
|
|
32
34
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
33
35
|
this.warn = options.warn ?? ((message) => log.warn(message));
|
|
34
36
|
this.now = options.now ?? (() => new Date());
|
|
37
|
+
this.modeContext = new ModeContext({
|
|
38
|
+
...config,
|
|
39
|
+
teamChapterhouseUrl: this.teamChapterhouseUrl,
|
|
40
|
+
standaloneMode: this.standaloneMode,
|
|
41
|
+
});
|
|
35
42
|
}
|
|
36
43
|
isEnabled() {
|
|
37
|
-
return
|
|
44
|
+
return this.modeContext.canSyncTeamWiki();
|
|
38
45
|
}
|
|
39
46
|
isTeamPath(path) {
|
|
40
47
|
return this.teamWikiPaths.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));
|
package/package.json
CHANGED