felenova-backlog 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +58 -0
  2. package/bin/backlog.js +2 -0
  3. package/dist/_core/criteria.d.ts +13 -0
  4. package/dist/_core/criteria.js +44 -0
  5. package/dist/_core/criteria.js.map +1 -0
  6. package/dist/_core/errors.d.ts +53 -0
  7. package/dist/_core/errors.js +91 -0
  8. package/dist/_core/errors.js.map +1 -0
  9. package/dist/_core/index.d.ts +24 -0
  10. package/dist/_core/index.js +27 -0
  11. package/dist/_core/index.js.map +1 -0
  12. package/dist/_core/loop.d.ts +73 -0
  13. package/dist/_core/loop.js +46 -0
  14. package/dist/_core/loop.js.map +1 -0
  15. package/dist/_core/markdown/items.d.ts +35 -0
  16. package/dist/_core/markdown/items.js +256 -0
  17. package/dist/_core/markdown/items.js.map +1 -0
  18. package/dist/_core/markdown/matrix.d.ts +20 -0
  19. package/dist/_core/markdown/matrix.js +147 -0
  20. package/dist/_core/markdown/matrix.js.map +1 -0
  21. package/dist/_core/markdown/track.d.ts +71 -0
  22. package/dist/_core/markdown/track.js +79 -0
  23. package/dist/_core/markdown/track.js.map +1 -0
  24. package/dist/_core/matrix-status.d.ts +9 -0
  25. package/dist/_core/matrix-status.js +31 -0
  26. package/dist/_core/matrix-status.js.map +1 -0
  27. package/dist/_core/matrix.d.ts +46 -0
  28. package/dist/_core/matrix.js +0 -0
  29. package/dist/_core/matrix.js.map +1 -0
  30. package/dist/_core/permissions.d.ts +14 -0
  31. package/dist/_core/permissions.js +87 -0
  32. package/dist/_core/permissions.js.map +1 -0
  33. package/dist/_core/promote.d.ts +20 -0
  34. package/dist/_core/promote.js +47 -0
  35. package/dist/_core/promote.js.map +1 -0
  36. package/dist/_core/ready.d.ts +8 -0
  37. package/dist/_core/ready.js +22 -0
  38. package/dist/_core/ready.js.map +1 -0
  39. package/dist/_core/review.d.ts +49 -0
  40. package/dist/_core/review.js +137 -0
  41. package/dist/_core/review.js.map +1 -0
  42. package/dist/_core/schema.d.ts +636 -0
  43. package/dist/_core/schema.js +304 -0
  44. package/dist/_core/schema.js.map +1 -0
  45. package/dist/_core/ship.d.ts +23 -0
  46. package/dist/_core/ship.js +31 -0
  47. package/dist/_core/ship.js.map +1 -0
  48. package/dist/_core/spec-diff.d.ts +19 -0
  49. package/dist/_core/spec-diff.js +25 -0
  50. package/dist/_core/spec-diff.js.map +1 -0
  51. package/dist/_core/status.d.ts +7 -0
  52. package/dist/_core/status.js +25 -0
  53. package/dist/_core/status.js.map +1 -0
  54. package/dist/_core/util/deep-equal.d.ts +8 -0
  55. package/dist/_core/util/deep-equal.js +31 -0
  56. package/dist/_core/util/deep-equal.js.map +1 -0
  57. package/dist/api.d.ts +46 -0
  58. package/dist/api.js +104 -0
  59. package/dist/api.js.map +1 -0
  60. package/dist/cache.d.ts +53 -0
  61. package/dist/cache.js +114 -0
  62. package/dist/cache.js.map +1 -0
  63. package/dist/commands/common.d.ts +59 -0
  64. package/dist/commands/common.js +94 -0
  65. package/dist/commands/common.js.map +1 -0
  66. package/dist/commands/export.d.ts +20 -0
  67. package/dist/commands/export.js +56 -0
  68. package/dist/commands/export.js.map +1 -0
  69. package/dist/commands/matrix.d.ts +16 -0
  70. package/dist/commands/matrix.js +48 -0
  71. package/dist/commands/matrix.js.map +1 -0
  72. package/dist/commands/next.d.ts +14 -0
  73. package/dist/commands/next.js +30 -0
  74. package/dist/commands/next.js.map +1 -0
  75. package/dist/commands/promote.d.ts +15 -0
  76. package/dist/commands/promote.js +172 -0
  77. package/dist/commands/promote.js.map +1 -0
  78. package/dist/commands/report.d.ts +18 -0
  79. package/dist/commands/report.js +45 -0
  80. package/dist/commands/report.js.map +1 -0
  81. package/dist/commands/ship.d.ts +15 -0
  82. package/dist/commands/ship.js +231 -0
  83. package/dist/commands/ship.js.map +1 -0
  84. package/dist/commands/status.d.ts +13 -0
  85. package/dist/commands/status.js +141 -0
  86. package/dist/commands/status.js.map +1 -0
  87. package/dist/commands/sync.d.ts +17 -0
  88. package/dist/commands/sync.js +90 -0
  89. package/dist/commands/sync.js.map +1 -0
  90. package/dist/conductor/registry.d.ts +45 -0
  91. package/dist/conductor/registry.js +164 -0
  92. package/dist/conductor/registry.js.map +1 -0
  93. package/dist/config.d.ts +47 -0
  94. package/dist/config.js +137 -0
  95. package/dist/config.js.map +1 -0
  96. package/dist/doctor.d.ts +28 -0
  97. package/dist/doctor.js +377 -0
  98. package/dist/doctor.js.map +1 -0
  99. package/dist/errors.d.ts +23 -0
  100. package/dist/errors.js +114 -0
  101. package/dist/errors.js.map +1 -0
  102. package/dist/export.d.ts +1 -0
  103. package/dist/export.js +9 -0
  104. package/dist/export.js.map +1 -0
  105. package/dist/import.d.ts +3 -0
  106. package/dist/import.js +89 -0
  107. package/dist/import.js.map +1 -0
  108. package/dist/index.d.ts +1 -0
  109. package/dist/index.js +183 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/init/auth.d.ts +85 -0
  112. package/dist/init/auth.js +194 -0
  113. package/dist/init/auth.js.map +1 -0
  114. package/dist/init/claudemd.d.ts +16 -0
  115. package/dist/init/claudemd.js +62 -0
  116. package/dist/init/claudemd.js.map +1 -0
  117. package/dist/init/index.d.ts +47 -0
  118. package/dist/init/index.js +159 -0
  119. package/dist/init/index.js.map +1 -0
  120. package/dist/init/mcp.d.ts +18 -0
  121. package/dist/init/mcp.js +97 -0
  122. package/dist/init/mcp.js.map +1 -0
  123. package/dist/init/plugin.d.ts +60 -0
  124. package/dist/init/plugin.js +120 -0
  125. package/dist/init/plugin.js.map +1 -0
  126. package/dist/init/scaffold.d.ts +19 -0
  127. package/dist/init/scaffold.js +46 -0
  128. package/dist/init/scaffold.js.map +1 -0
  129. package/dist/init/templates.d.ts +48 -0
  130. package/dist/init/templates.js +147 -0
  131. package/dist/init/templates.js.map +1 -0
  132. package/dist/plugin/.claude-plugin/plugin.json +11 -0
  133. package/dist/plugin/README.md +117 -0
  134. package/dist/plugin/commands/next.md +28 -0
  135. package/dist/plugin/commands/promote.md +30 -0
  136. package/dist/plugin/commands/report.md +26 -0
  137. package/dist/plugin/commands/ship.md +30 -0
  138. package/dist/plugin/skills/backlog-management/SKILL.md +63 -0
  139. package/dist/plugin/skills/backlog-query/SKILL.md +57 -0
  140. package/dist/plugin/skills/feature-kickoff/SKILL.md +112 -0
  141. package/dist/snapshot.d.ts +24 -0
  142. package/dist/snapshot.js +2 -0
  143. package/dist/snapshot.js.map +1 -0
  144. package/dist/token.d.ts +29 -0
  145. package/dist/token.js +99 -0
  146. package/dist/token.js.map +1 -0
  147. package/package.json +52 -0
@@ -0,0 +1,194 @@
1
+ import { createInterface } from 'node:readline';
2
+ import { Writable } from 'node:stream';
3
+ import { ApiError, get } from '../api.js';
4
+ import { CONFIG_FILENAME } from '../config.js';
5
+ import { explain } from '../errors.js';
6
+ import { readToken, tokenPath } from '../token.js';
7
+ /**
8
+ * The token and the project. Both are decided before `init` writes anything: a refusal
9
+ * here must leave the repo exactly as it was, including the token file.
10
+ *
11
+ * There is no device flow — the service mints PATs in the web UI and nothing else. So the
12
+ * only interactive path is a hidden paste.
13
+ */
14
+ /** A refusal `init` renders as one line. */
15
+ export class InitError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = 'InitError';
19
+ }
20
+ }
21
+ /**
22
+ * Reads one line with the echo suppressed: the PAT never reaches the terminal, and so
23
+ * never reaches a scrollback buffer or a screen recording.
24
+ *
25
+ * The mute is a discarding output stream rather than an override of readline's internal
26
+ * `_writeToOutput` — that hook does not catch the per-character echo a terminal-mode
27
+ * interface writes, and the token would appear one character at a time.
28
+ */
29
+ export async function promptHidden(prompt, io) {
30
+ const discard = new Writable({
31
+ write(_chunk, _encoding, done) {
32
+ done();
33
+ },
34
+ });
35
+ const rl = createInterface({ input: io.input, output: discard, terminal: true });
36
+ io.output.write(prompt);
37
+ try {
38
+ return await new Promise((resolve) => {
39
+ rl.question('', (answer) => resolve(answer.trim()));
40
+ });
41
+ }
42
+ finally {
43
+ rl.close();
44
+ io.output.write('\n');
45
+ }
46
+ }
47
+ /** Reads one visible line. */
48
+ export async function promptLine(prompt, io) {
49
+ const rl = createInterface({ input: io.input, output: io.output, terminal: true });
50
+ try {
51
+ return await new Promise((resolve) => {
52
+ rl.question(prompt, (answer) => resolve(answer.trim()));
53
+ });
54
+ }
55
+ finally {
56
+ rl.close();
57
+ }
58
+ }
59
+ /**
60
+ * `--token` → `$BACKLOG_TOKEN` → `~/.config/backlog/token` → an interactive paste.
61
+ * Never logged, never echoed, never returned inside a message.
62
+ */
63
+ export async function resolveInitToken(opts) {
64
+ const fromFlag = opts.flagToken?.trim();
65
+ if (fromFlag)
66
+ return { token: fromFlag, source: 'flag' };
67
+ const fromEnv = opts.env.BACKLOG_TOKEN?.trim();
68
+ if (fromEnv)
69
+ return { token: fromEnv, source: 'env' };
70
+ const fromFile = readToken(opts.home);
71
+ if (fromFile)
72
+ return { token: fromFile, source: 'file' };
73
+ if (!opts.io.interactive) {
74
+ throw new InitError(`no PAT available — pass --token, set BACKLOG_TOKEN, or put one in ${tokenPath(opts.home)} (mint it at ${opts.url}/me → "Mint a token")`);
75
+ }
76
+ const pasted = await promptHidden(`Paste a PAT (minted at ${opts.url}/me → "Mint a token"): `, opts.io);
77
+ if (!pasted)
78
+ throw new InitError('no PAT entered — nothing was written');
79
+ return { token: pasted, source: 'prompt' };
80
+ }
81
+ function parseProjects(body) {
82
+ if (!Array.isArray(body)) {
83
+ throw new InitError('the service returned a malformed project list — run: backlog doctor');
84
+ }
85
+ return body.map((raw) => {
86
+ const row = (raw ?? {});
87
+ const slug = typeof row.slug === 'string' ? row.slug : '';
88
+ if (!slug) {
89
+ throw new InitError('the service returned a project with no slug — run: backlog doctor');
90
+ }
91
+ return {
92
+ slug,
93
+ name: typeof row.name === 'string' ? row.name : slug,
94
+ role: typeof row.role === 'string' ? row.role : 'unknown',
95
+ };
96
+ });
97
+ }
98
+ /**
99
+ * `GET /v1/projects` — which is also the token's validation. A 401/403 renders through
100
+ * `explain()`: one sentence, no status code, no JSON.
101
+ */
102
+ export async function fetchVisibleProjects(opts) {
103
+ try {
104
+ const body = await get({ url: opts.url, fetchImpl: opts.fetchImpl }, opts.token, '/v1/projects');
105
+ return parseProjects(body);
106
+ }
107
+ catch (err) {
108
+ if (err instanceof ApiError)
109
+ throw new InitError(explain(err, { url: opts.url }));
110
+ throw err;
111
+ }
112
+ }
113
+ function slugList(projects) {
114
+ return projects.map((p) => p.slug).join(', ');
115
+ }
116
+ /**
117
+ * Only what `GET /v1/projects` returned is offerable (SVC-008): a slug that is not in the
118
+ * list is a membership problem, and the CLI says so in the same words `doctor` uses.
119
+ *
120
+ * The order is `--project` → the project an existing `backlog.config.json` already names →
121
+ * the only visible project → a prompt. `configured` is what makes a re-run in a repo that
122
+ * is already set up non-interactive-safe: the repo has already answered "which project",
123
+ * and re-asking (or refusing because several are visible) would make `init` unusable in CI
124
+ * and annoying everywhere else. It is a *default*, not an override: `--project` still wins,
125
+ * and a configured project the token can no longer see is refused exactly like a typed one
126
+ * — silently falling back to a different project would point the repo's commands at the
127
+ * wrong register.
128
+ */
129
+ export async function chooseProject(opts) {
130
+ const { projects, requested } = opts;
131
+ if (projects.length === 0) {
132
+ throw new InitError(`this token can see no projects — ask the Owner for a role on one (${opts.url}/me shows what it can reach)`);
133
+ }
134
+ const refuseUnseen = (slug, source) => {
135
+ const sentence = explain(new ApiError(404, 'not_found', ''), { project: slug, url: opts.url });
136
+ throw new InitError(`${sentence}${source}; visible to this token: ${slugList(projects)}`);
137
+ };
138
+ if (requested?.trim()) {
139
+ const slug = requested.trim();
140
+ const match = projects.find((p) => p.slug === slug);
141
+ if (match)
142
+ return match;
143
+ refuseUnseen(slug, '');
144
+ }
145
+ const configured = opts.configured?.trim();
146
+ if (configured) {
147
+ const match = projects.find((p) => p.slug === configured);
148
+ if (match)
149
+ return match;
150
+ refuseUnseen(configured, ` (named in ${CONFIG_FILENAME} — pass --project <slug> to point this repo somewhere else)`);
151
+ }
152
+ if (projects.length === 1) {
153
+ const only = projects[0];
154
+ if (!only)
155
+ throw new InitError('the service returned an empty project list');
156
+ return only;
157
+ }
158
+ if (!opts.io.interactive) {
159
+ throw new InitError(`several projects are visible (${slugList(projects)}) — re-run with --project <slug>`);
160
+ }
161
+ opts.io.output.write('Projects this token can see:\n');
162
+ projects.forEach((p, i) => {
163
+ opts.io.output.write(` ${i + 1}) ${p.slug} — ${p.name} (${p.role})\n`);
164
+ });
165
+ const answer = await promptLine(`Pick one [1-${projects.length}]: `, opts.io);
166
+ const index = Number.parseInt(answer, 10);
167
+ const picked = Number.isInteger(index) ? projects[index - 1] : undefined;
168
+ if (!picked) {
169
+ throw new InitError(`"${answer}" is not one of 1-${projects.length} — re-run and pick a number, or pass --project <slug>`);
170
+ }
171
+ return picked;
172
+ }
173
+ /** Whether to persist the PAT: the flag if given, else an interactive y/N, else no. */
174
+ export async function shouldSaveToken(opts) {
175
+ if (opts.flag !== undefined)
176
+ return opts.flag;
177
+ // It is already there, and `readToken` proved the mode. Nothing to consent to.
178
+ if (opts.source === 'file')
179
+ return false;
180
+ if (opts.yes)
181
+ return true;
182
+ // Only the human who just pasted a PAT is asked. A token that arrived by flag or in the
183
+ // environment is somebody's deliberate, possibly throwaway choice — persisting it needs
184
+ // `--save-token`, not a question nobody expected.
185
+ if (opts.source !== 'prompt')
186
+ return false;
187
+ if (!opts.io.interactive)
188
+ return false;
189
+ const answer = await promptLine(`Save the PAT to ${tokenPath(opts.home)} (mode 600)? [y/N] `, {
190
+ ...opts.io,
191
+ });
192
+ return /^y(es)?$/i.test(answer.trim());
193
+ }
194
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/init/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type ClaudeSectionContext } from './templates.js';
2
+ /**
3
+ * The section is marker-delimited so a second `init` can update it without a diff, and so
4
+ * the rest of somebody's `CLAUDE.md` is never in play. An *unmarked* "Backlog → Conductor"
5
+ * heading is someone's hand-written prose: it is left exactly as it is and reported.
6
+ */
7
+ export declare const CLAUDE_FILENAME = "CLAUDE.md";
8
+ export type ClaudeOutcome = 'created' | 'appended' | 'updated' | 'unchanged' | 'legacy';
9
+ export interface ClaudeResult {
10
+ outcome: ClaudeOutcome;
11
+ line: string;
12
+ }
13
+ export declare function writeClaudeSection(opts: {
14
+ cwd: string;
15
+ ctx: ClaudeSectionContext;
16
+ }): ClaudeResult;
@@ -0,0 +1,62 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { InitError } from './auth.js';
4
+ import { claudeSection, END_MARKER, LEGACY_HEADING, START_MARKER, } from './templates.js';
5
+ /**
6
+ * The section is marker-delimited so a second `init` can update it without a diff, and so
7
+ * the rest of somebody's `CLAUDE.md` is never in play. An *unmarked* "Backlog → Conductor"
8
+ * heading is someone's hand-written prose: it is left exactly as it is and reported.
9
+ */
10
+ export const CLAUDE_FILENAME = 'CLAUDE.md';
11
+ export function writeClaudeSection(opts) {
12
+ const path = join(opts.cwd, CLAUDE_FILENAME);
13
+ const block = claudeSection(opts.ctx);
14
+ let text = null;
15
+ try {
16
+ text = readFileSync(path, 'utf8');
17
+ }
18
+ catch {
19
+ text = null;
20
+ }
21
+ if (text === null) {
22
+ writeFileSync(path, block, 'utf8');
23
+ return {
24
+ outcome: 'created',
25
+ line: `${CLAUDE_FILENAME}: created with the Backlog → Conductor section`,
26
+ };
27
+ }
28
+ const start = text.indexOf(START_MARKER);
29
+ const end = text.indexOf(END_MARKER);
30
+ if (start !== -1 && end === -1) {
31
+ throw new InitError(`${path} has ${START_MARKER} but no ${END_MARKER} — close the block by hand; init will not guess where it ends`);
32
+ }
33
+ if (start !== -1 && end < start) {
34
+ throw new InitError(`${path} has ${END_MARKER} before ${START_MARKER} — fix the markers by hand`);
35
+ }
36
+ if (start !== -1) {
37
+ const after = end + END_MARKER.length;
38
+ const current = text.slice(start, after);
39
+ const replacement = block.trimEnd();
40
+ if (current === replacement) {
41
+ return { outcome: 'unchanged', line: `${CLAUDE_FILENAME}: section already up to date` };
42
+ }
43
+ writeFileSync(path, `${text.slice(0, start)}${replacement}${text.slice(after)}`, 'utf8');
44
+ return {
45
+ outcome: 'updated',
46
+ line: `${CLAUDE_FILENAME}: section updated in place (only the text between the markers)`,
47
+ };
48
+ }
49
+ if (text.includes(LEGACY_HEADING)) {
50
+ return {
51
+ outcome: 'legacy',
52
+ line: `${CLAUDE_FILENAME}: an unmarked "Backlog → Conductor" heading is already there — left alone; delete it if you want init to manage the section`,
53
+ };
54
+ }
55
+ const prefix = text === '' || text.endsWith('\n') ? text : `${text}\n`;
56
+ writeFileSync(path, `${prefix}\n${block}`, 'utf8');
57
+ return {
58
+ outcome: 'appended',
59
+ line: `${CLAUDE_FILENAME}: Backlog → Conductor section appended`,
60
+ };
61
+ }
62
+ //# sourceMappingURL=claudemd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claudemd.js","sourceRoot":"","sources":["../../src/init/claudemd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ export { InitError } from './auth.js';
2
+ /**
3
+ * `backlog init` — the one command that turns a repo into a client of the register.
4
+ *
5
+ * Two invariants shape the order below. First, **nothing is written until the token and
6
+ * the project are settled**: a refusal (a rejected PAT, a project the token cannot see)
7
+ * must leave the repo, and `~/.config/backlog/token`, exactly as they were. Second,
8
+ * **every write is create-if-missing, merge, or refuse** — `init` never overwrites a file
9
+ * it did not write, and a second run is a byte-for-byte no-op that prints what it left
10
+ * alone (DECISION-027).
11
+ *
12
+ * The doctor runs last and decides only the exit code, after everything is on disk: a
13
+ * failing check is a diagnosis, not a reason to leave the install half-done.
14
+ */
15
+ export interface InitFlags {
16
+ url?: string | undefined;
17
+ project?: string | undefined;
18
+ token?: string | undefined;
19
+ /** `--save-token` / `--no-save-token`; undefined means "ask, or don't". */
20
+ saveToken?: boolean | undefined;
21
+ /** `--yes`: never prompt, and consent to storing a pasted PAT. */
22
+ yes?: boolean | undefined;
23
+ /** `--force-mcp`: replace a differing register server entry (`MCP_SERVER_KEY`). */
24
+ forceMcp?: boolean | undefined;
25
+ /** `--force-plugin`: replace a payload file that exists with different content. */
26
+ forcePlugin?: boolean | undefined;
27
+ }
28
+ export interface InitOptions {
29
+ cwd: string;
30
+ home: string;
31
+ env: NodeJS.ProcessEnv;
32
+ flags: InitFlags;
33
+ fetchImpl?: typeof fetch | undefined;
34
+ input?: NodeJS.ReadableStream | undefined;
35
+ output?: NodeJS.WritableStream | undefined;
36
+ /** Overrides TTY detection; `--yes` forces it off. */
37
+ interactive?: boolean | undefined;
38
+ }
39
+ export interface InitResult {
40
+ /** The doctor's verdict — the process exit code comes from this, and only this. */
41
+ ok: boolean;
42
+ lines: string[];
43
+ project: string;
44
+ url: string;
45
+ conductorDir: string;
46
+ }
47
+ export declare function runInit(opts: InitOptions): Promise<InitResult>;
@@ -0,0 +1,159 @@
1
+ import { join } from 'node:path';
2
+ import { CONFIG_FILENAME, ConfigError, loadConfig, writeConfig } from '../config.js';
3
+ import { runDoctorRemote } from '../doctor.js';
4
+ import { saveToken, tokenPath } from '../token.js';
5
+ import { chooseProject, fetchVisibleProjects, InitError, resolveInitToken, shouldSaveToken, } from './auth.js';
6
+ import { writeClaudeSection } from './claudemd.js';
7
+ import { writeMcpJson } from './mcp.js';
8
+ import { installPlugin } from './plugin.js';
9
+ import { scaffoldConductor, updateGitignore } from './scaffold.js';
10
+ import { DEFAULT_CONDUCTOR_DIR, DEFAULT_URL } from './templates.js';
11
+ export { InitError } from './auth.js';
12
+ function describeTokenSource(source, home) {
13
+ switch (source) {
14
+ case 'flag':
15
+ return 'read from --token';
16
+ case 'env':
17
+ return 'read from BACKLOG_TOKEN';
18
+ case 'file':
19
+ return `read from ${tokenPath(home)}`;
20
+ default:
21
+ return 'read from the prompt';
22
+ }
23
+ }
24
+ function existingConfig(cwd) {
25
+ // A config file directly in `cwd` is the one `init` owns. A broken one is reported by
26
+ // its own message rather than being silently replaced.
27
+ const path = join(cwd, CONFIG_FILENAME);
28
+ try {
29
+ const loaded = loadConfig(cwd);
30
+ if (!loaded || loaded.path !== path)
31
+ return null;
32
+ return { ...loaded.config, path: loaded.path };
33
+ }
34
+ catch (err) {
35
+ if (err instanceof ConfigError) {
36
+ throw new InitError(`${err.message} — fix or delete ${err.path}, then run init again (init never rewrites a config it cannot read)`);
37
+ }
38
+ throw err;
39
+ }
40
+ }
41
+ export async function runInit(opts) {
42
+ const lines = [];
43
+ const output = opts.output ?? process.stdout;
44
+ const say = (line) => {
45
+ lines.push(line);
46
+ output.write(`${line}\n`);
47
+ };
48
+ const inTty = Boolean(opts.input?.isTTY && output.isTTY);
49
+ const io = {
50
+ input: opts.input ?? process.stdin,
51
+ output,
52
+ interactive: opts.flags.yes ? false : (opts.interactive ?? inTty),
53
+ };
54
+ const prior = existingConfig(opts.cwd);
55
+ // 2. the service URL: --url → BACKLOG_URL → an existing config → the shipped default
56
+ const url = (opts.flags.url?.trim() ||
57
+ opts.env.BACKLOG_URL?.trim() ||
58
+ prior?.url ||
59
+ DEFAULT_URL).replace(/\/+$/, '');
60
+ // 1. the token, still without writing anything
61
+ const token = await resolveInitToken({
62
+ flagToken: opts.flags.token,
63
+ env: opts.env,
64
+ home: opts.home,
65
+ url,
66
+ io,
67
+ });
68
+ say(`token: ${describeTokenSource(token.source, opts.home)}`);
69
+ say(`url: ${url}`);
70
+ // 3. only what GET /v1/projects returns is offerable (SVC-008)
71
+ const projects = await fetchVisibleProjects({
72
+ url,
73
+ token: token.token,
74
+ fetchImpl: opts.fetchImpl,
75
+ });
76
+ say(`projects: ${projects.length} visible to this token (${projects.map((p) => p.slug).join(', ')})`);
77
+ const project = await chooseProject({
78
+ requested: opts.flags.project,
79
+ // The repo's existing answer is the default when no flag gives one (Task 6's finding):
80
+ // a re-run must not have to re-choose, and must not refuse just because the token can
81
+ // see more than one project.
82
+ configured: prior?.project,
83
+ projects,
84
+ url,
85
+ io,
86
+ });
87
+ // Say where the choice came from when it came from the config, so a re-run that picks a
88
+ // project without asking is not mistaken for a guess.
89
+ const fromConfig = !opts.flags.project?.trim() && prior?.project === project.slug;
90
+ say(`project: ${project.slug} — ${project.name} (${project.role})${fromConfig ? ` — from ${CONFIG_FILENAME}` : ''}`);
91
+ // the token is proven now, so persisting it is finally safe
92
+ const save = await shouldSaveToken({
93
+ flag: opts.flags.saveToken,
94
+ yes: opts.flags.yes,
95
+ source: token.source,
96
+ home: opts.home,
97
+ io,
98
+ });
99
+ if (save) {
100
+ const path = saveToken(opts.home, token.token);
101
+ say(`token: saved to ${path} (mode 600)`);
102
+ }
103
+ else if (token.source === 'file') {
104
+ say(`token: already at ${tokenPath(opts.home)}`);
105
+ }
106
+ else {
107
+ say(`token: not saved — to keep it: printf '%s\\n' "$BACKLOG_TOKEN" > ${tokenPath(opts.home)} && chmod 600 ${tokenPath(opts.home)}`);
108
+ }
109
+ // 4. backlog.config.json — created, or updated in place with only the three keys
110
+ const conductorDir = prior?.conductor_dir ?? DEFAULT_CONDUCTOR_DIR;
111
+ const configPath = join(opts.cwd, CONFIG_FILENAME);
112
+ const wanted = { url, project: project.slug, conductor_dir: conductorDir };
113
+ if (!prior) {
114
+ writeConfig(configPath, wanted);
115
+ say(`${CONFIG_FILENAME}: created (url, project, conductor_dir)`);
116
+ }
117
+ else if (prior.url === wanted.url &&
118
+ prior.project === wanted.project &&
119
+ prior.conductor_dir === wanted.conductor_dir) {
120
+ say(`${CONFIG_FILENAME}: unchanged`);
121
+ }
122
+ else {
123
+ writeConfig(configPath, wanted);
124
+ say(`${CONFIG_FILENAME}: updated in place (project ${project.slug}, conductor_dir ${conductorDir})`);
125
+ }
126
+ // 5. .mcp.json — create, or merge without touching another tool's server
127
+ say(writeMcpJson({ cwd: opts.cwd, url, force: Boolean(opts.flags.forceMcp) }).line);
128
+ say(updateGitignore(opts.cwd));
129
+ // 6. the conductor scaffold — create-if-missing, file by file
130
+ for (const line of scaffoldConductor({ cwd: opts.cwd, conductorDir }).lines)
131
+ say(line);
132
+ // 7. CLAUDE.md — one marked section, updated in place on a second run
133
+ say(writeClaudeSection({
134
+ cwd: opts.cwd,
135
+ ctx: { url, project: project.slug, conductorDir },
136
+ }).line);
137
+ // 8. the plugin payload — one line per file, then the summary
138
+ for (const line of installPlugin({ cwd: opts.cwd, force: Boolean(opts.flags.forcePlugin) })
139
+ .lines) {
140
+ say(line);
141
+ }
142
+ // 9. the doctor, last: it decides the exit code, never what gets written
143
+ const report = await runDoctorRemote(opts.env, {
144
+ cwd: opts.cwd,
145
+ home: opts.home,
146
+ url,
147
+ project: project.slug,
148
+ ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),
149
+ });
150
+ say('doctor:');
151
+ for (const check of report.checks) {
152
+ // Same rendering as the `doctor` command: a hint is advice about a failure, so a
153
+ // passing check prints its name and nothing else.
154
+ const hint = check.ok || !check.hint ? '' : ` — ${check.hint}`;
155
+ say(` ${check.ok ? 'ok ' : 'FAIL'} ${check.name}${hint}`);
156
+ }
157
+ return { ok: report.ok, lines, project: project.slug, url, conductorDir };
158
+ }
159
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/init/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `.mcp.json` is a file other tools own too — every other server in it belongs to someone
3
+ * else. So this is a merge, never a rewrite: add the `backlog` key when it is absent, keep
4
+ * every other key exactly as it was, and refuse a differing `backlog` entry rather than
5
+ * silently re-pointing somebody's MCP client.
6
+ */
7
+ export declare const MCP_FILENAME = ".mcp.json";
8
+ export type McpOutcome = 'created' | 'added' | 'identical' | 'replaced';
9
+ export interface McpResult {
10
+ outcome: McpOutcome;
11
+ line: string;
12
+ }
13
+ /** Create or merge. Returns the line `init` prints; throws when it would have to clobber. */
14
+ export declare function writeMcpJson(opts: {
15
+ cwd: string;
16
+ url: string;
17
+ force: boolean;
18
+ }): McpResult;
@@ -0,0 +1,97 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { InitError } from './auth.js';
4
+ import { MCP_SERVER_KEY, mcpServerEntry } from './templates.js';
5
+ /**
6
+ * `.mcp.json` is a file other tools own too — every other server in it belongs to someone
7
+ * else. So this is a merge, never a rewrite: add the `backlog` key when it is absent, keep
8
+ * every other key exactly as it was, and refuse a differing `backlog` entry rather than
9
+ * silently re-pointing somebody's MCP client.
10
+ */
11
+ export const MCP_FILENAME = '.mcp.json';
12
+ function stable(value) {
13
+ return JSON.stringify(value);
14
+ }
15
+ function describeDifference(existing, wanted) {
16
+ const lines = [];
17
+ const row = (existing ?? {});
18
+ const url = row.url;
19
+ if (url !== wanted.url) {
20
+ lines.push(` url: ${typeof url === 'string' ? url : stable(url)} → ${wanted.url}`);
21
+ }
22
+ const type = row.type;
23
+ if (type !== wanted.type) {
24
+ lines.push(` type: ${typeof type === 'string' ? type : stable(type)} → ${wanted.type}`);
25
+ }
26
+ const headers = (row.headers ?? {});
27
+ const auth = headers.Authorization;
28
+ if (auth !== wanted.headers.Authorization) {
29
+ // The wanted value is the literal placeholder; an existing one is only ever echoed
30
+ // when it is *not* a token-shaped string.
31
+ const shown = typeof auth === 'string' && /bkl_|secret/i.test(auth) ? '<a literal value>' : stable(auth);
32
+ lines.push(` headers.Authorization: ${shown} → ${stable(wanted.headers.Authorization)}`);
33
+ }
34
+ if (lines.length === 0)
35
+ lines.push(` ${stable(existing)} → ${stable(wanted)}`);
36
+ return lines.join('\n');
37
+ }
38
+ /** Create or merge. Returns the line `init` prints; throws when it would have to clobber. */
39
+ export function writeMcpJson(opts) {
40
+ const path = join(opts.cwd, MCP_FILENAME);
41
+ const wanted = mcpServerEntry(opts.url);
42
+ let text = null;
43
+ try {
44
+ text = readFileSync(path, 'utf8');
45
+ }
46
+ catch {
47
+ text = null;
48
+ }
49
+ if (text === null) {
50
+ const doc = { mcpServers: { [MCP_SERVER_KEY]: wanted } };
51
+ writeFileSync(path, `${JSON.stringify(doc, null, 2)}\n`, 'utf8');
52
+ return {
53
+ outcome: 'created',
54
+ line: `${MCP_FILENAME}: created with the ${MCP_SERVER_KEY} server entry (${wanted.headers.Authorization})`,
55
+ };
56
+ }
57
+ let doc;
58
+ try {
59
+ const parsed = JSON.parse(text);
60
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
61
+ throw new Error('not an object');
62
+ }
63
+ doc = parsed;
64
+ }
65
+ catch {
66
+ throw new InitError(`${path} is not a JSON object — fix it by hand; init never rewrites an existing ${MCP_FILENAME}`);
67
+ }
68
+ const rawServers = doc.mcpServers;
69
+ if (rawServers !== undefined && (typeof rawServers !== 'object' || rawServers === null)) {
70
+ throw new InitError(`${path} has an "mcpServers" that is not an object — fix it by hand; init never rewrites an existing ${MCP_FILENAME}`);
71
+ }
72
+ const servers = { ...(rawServers ?? {}) };
73
+ const existing = servers[MCP_SERVER_KEY];
74
+ if (existing !== undefined) {
75
+ if (stable(existing) === stable(wanted)) {
76
+ return {
77
+ outcome: 'identical',
78
+ line: `${MCP_FILENAME}: ${MCP_SERVER_KEY} entry already correct — left alone`,
79
+ };
80
+ }
81
+ if (!opts.force) {
82
+ throw new InitError(`${path} already has a different "${MCP_SERVER_KEY}" server entry:\n${describeDifference(existing, wanted)}\nedit ${MCP_FILENAME} yourself, or re-run with --force-mcp to replace just that entry`);
83
+ }
84
+ }
85
+ servers[MCP_SERVER_KEY] = wanted;
86
+ writeFileSync(path, `${JSON.stringify({ ...doc, mcpServers: servers }, null, 2)}\n`, 'utf8');
87
+ return existing === undefined
88
+ ? {
89
+ outcome: 'added',
90
+ line: `${MCP_FILENAME}: added the ${MCP_SERVER_KEY} server entry (${wanted.headers.Authorization}); every other server left as it was`,
91
+ }
92
+ : {
93
+ outcome: 'replaced',
94
+ line: `${MCP_FILENAME}: replaced the ${MCP_SERVER_KEY} server entry (--force-mcp); every other server left as it was`,
95
+ };
96
+ }
97
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/init/mcp.ts"],"names":[],"mappings":"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"}