intentdocs 0.8.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.
@@ -0,0 +1,621 @@
1
+ import {
2
+ writeStoryContext
3
+ } from "./chunk-6FGYJ3W2.js";
4
+ import {
5
+ choose,
6
+ closePrompt
7
+ } from "./chunk-WPVDURTJ.js";
8
+ import {
9
+ apiFetch,
10
+ getContext,
11
+ listStories
12
+ } from "./chunk-6XKB6RTU.js";
13
+ import {
14
+ hasConfig,
15
+ requireConfig
16
+ } from "./chunk-ICOMMHKG.js";
17
+ import {
18
+ getAuth,
19
+ requireAuth
20
+ } from "./chunk-PCNDP5SX.js";
21
+
22
+ // src/commands/setup.ts
23
+ import { existsSync as existsSync2, mkdirSync, writeFileSync as writeFileSync2, readFileSync, unlinkSync } from "fs";
24
+ import { join as join2, dirname } from "path";
25
+ import { homedir } from "os";
26
+
27
+ // src/commands/sync.ts
28
+ import { writeFileSync, readdirSync, existsSync, statSync } from "fs";
29
+ import { join } from "path";
30
+
31
+ // src/lib/analytics.ts
32
+ import { PostHog } from "posthog-node";
33
+ var client = null;
34
+ function getClient() {
35
+ if (client) return client;
36
+ const auth = getAuth();
37
+ if (!auth?.posthogKey) return null;
38
+ client = new PostHog(auth.posthogKey, {
39
+ host: auth.posthogHost ?? "https://eu.i.posthog.com",
40
+ flushAt: 1,
41
+ flushInterval: 0
42
+ });
43
+ return client;
44
+ }
45
+ function getDistinctId() {
46
+ return getAuth()?.userId ?? null;
47
+ }
48
+ function captureCli(event, properties, groups) {
49
+ const posthog = getClient();
50
+ const distinctId = getDistinctId();
51
+ if (!posthog || !distinctId) return;
52
+ posthog.capture({
53
+ distinctId,
54
+ event,
55
+ properties: {
56
+ ...properties,
57
+ ...groups?.project ? { project_id: groups.project } : {}
58
+ },
59
+ ...groups ? { groups } : {}
60
+ });
61
+ }
62
+ async function shutdownCliAnalytics() {
63
+ if (client) await client.shutdown();
64
+ }
65
+
66
+ // src/commands/sync.ts
67
+ async function syncContextFile(config) {
68
+ const res = await apiFetch(config, "/context-file?format=claude");
69
+ const { content } = await res.json();
70
+ const cwd = process.cwd();
71
+ const claudeDir = join(cwd, ".claude");
72
+ let filePath;
73
+ if (existsSync(claudeDir)) {
74
+ filePath = join(claudeDir, "CLAUDE.md");
75
+ } else {
76
+ filePath = join(cwd, "CLAUDE.md");
77
+ }
78
+ writeFileSync(filePath, content, "utf-8");
79
+ const rel = filePath.replace(cwd + "/", "");
80
+ console.log(`\u2713 ${rel} generated from intentdocs project`);
81
+ }
82
+ async function sync(opts) {
83
+ const config = requireConfig();
84
+ if (opts?.context) {
85
+ console.log("Refreshing context file from intentdocs...");
86
+ try {
87
+ await syncContextFile(config);
88
+ captureCli(
89
+ "claude_md_snapshot_created",
90
+ { trigger: "stc_sync_context" },
91
+ { project: config.projectId }
92
+ );
93
+ await shutdownCliAnalytics();
94
+ } catch (err) {
95
+ console.error("\u2717 Failed to refresh context file:", err.message);
96
+ process.exit(1);
97
+ }
98
+ return;
99
+ }
100
+ console.log("Syncing with intentdocs...");
101
+ const [md, data] = await Promise.all([
102
+ getContext(config),
103
+ listStories(config)
104
+ ]);
105
+ writeFileSync(join(process.cwd(), "INTENT.md"), md);
106
+ const { summary } = data;
107
+ const pct = summary.total > 0 ? Math.round(summary.done / summary.total * 100) : 0;
108
+ console.log(`\u2713 INTENT.md updated (${summary.done}/${summary.total} done, ${pct}%)`);
109
+ apiFetch(config, "/activity", {
110
+ method: "POST",
111
+ headers: { "Content-Type": "application/json" },
112
+ body: JSON.stringify({
113
+ event_type: "sync",
114
+ summary: `Synced story map \u2014 ${summary.done}/${summary.total} stories done (${pct}%)`,
115
+ source: "cli"
116
+ })
117
+ }).catch(() => void 0);
118
+ const contextDir = join(process.cwd(), ".intent", "stories");
119
+ if (!existsSync(contextDir)) return;
120
+ const localFiles = readdirSync(contextDir).filter((f) => f.endsWith(".md"));
121
+ if (localFiles.length === 0) return;
122
+ const storyMap = new Map(data.stories.map((s) => [s.id, s]));
123
+ let refreshed = 0;
124
+ let stale = 0;
125
+ for (const filename of localFiles) {
126
+ const storyId = filename.replace(".md", "");
127
+ const story = storyMap.get(storyId);
128
+ if (!story) {
129
+ stale++;
130
+ continue;
131
+ }
132
+ writeStoryContext(story);
133
+ refreshed++;
134
+ }
135
+ if (refreshed > 0 || stale > 0) {
136
+ console.log(`\u2713 .intent/stories/ refreshed (${refreshed} updated${stale > 0 ? `, ${stale} orphaned` : ""})`);
137
+ if (stale > 0) {
138
+ console.log(` \u26A0 ${stale} context file(s) have no matching story \u2014 they may have been deleted in the UI.`);
139
+ }
140
+ }
141
+ const specPath = join(process.cwd(), "INTENT.md");
142
+ const specBytes = existsSync(specPath) ? statSync(specPath).size : 0;
143
+ captureCli(
144
+ "harness_health_computed",
145
+ {
146
+ completeness_pct: pct,
147
+ stories_total: summary.total,
148
+ stories_done: summary.done,
149
+ spec_bytes: specBytes,
150
+ context_files_refreshed: refreshed
151
+ },
152
+ { project: config.projectId }
153
+ );
154
+ await shutdownCliAnalytics();
155
+ }
156
+
157
+ // src/commands/setup.ts
158
+ async function setup() {
159
+ requireAuth();
160
+ if (!hasConfig()) {
161
+ console.log(" Not connected to a project. Run `intent init` first.");
162
+ closePrompt();
163
+ return;
164
+ }
165
+ const config = requireConfig();
166
+ const idx = await choose(" Which AI tool do you want to configure?", [
167
+ "Claude Code",
168
+ "Cursor",
169
+ "Both"
170
+ ]);
171
+ const tools = idx === 0 ? ["claude"] : idx === 1 ? ["cursor"] : ["claude", "cursor"];
172
+ for (const tool of tools) {
173
+ if (tool === "claude") setupClaudeCode(config.syncToken);
174
+ if (tool === "cursor") setupCursor(config.syncToken);
175
+ }
176
+ try {
177
+ await syncContextFile(config);
178
+ } catch {
179
+ console.log(" \u26A0 Could not fetch project context (run `intent sync --context` to retry)");
180
+ }
181
+ generateAgentDocs();
182
+ console.log(`
183
+ \u2705 Agent configuration complete!
184
+
185
+ Your AI tool now has:
186
+ \u2022 Full project context: instructions, scope, decisions, examples, tools, guardrails
187
+ \u2022 Slash commands for the full development workflow
188
+ \u2022 Agent guides for implementing, reviewing, and documenting features
189
+
190
+ Slash commands (inside Claude Code / Cursor):
191
+ /intent-next Pick up the next story
192
+ /intent-implement Full implementation playbook
193
+ /intent-review Review against acceptance criteria
194
+ /intent-done Mark a story done + log a decision
195
+ /intent-propose Propose a new change
196
+ /intent-sync Refresh context from intentdocs
197
+
198
+ Terminal commands:
199
+ intent next Next priority story
200
+ intent done <id> Mark done + log decision/lesson
201
+ intent decide Log an architectural decision
202
+ intent example Log a reference pattern
203
+ intent sync Refresh INTENT.md
204
+ intent sync --context Regenerate CLAUDE.md from intentdocs
205
+ intent status Recent activity
206
+ `);
207
+ closePrompt();
208
+ }
209
+ function setupClaudeCode(syncToken) {
210
+ const cwd = process.cwd();
211
+ const claudeMdPath = join2(cwd, "CLAUDE.md");
212
+ const claudeBlock = `
213
+ # intentdocs Integration
214
+
215
+ This project is connected to a intentdocs story map.
216
+ Run \`intent sync --context\` to generate the full CLAUDE.md with your project's instructions,
217
+ story map, decisions, examples, tools, and guardrails.
218
+
219
+ ## Quick reference
220
+
221
+ | Command | What it does |
222
+ |---|---|
223
+ | \`intent next\` | Pick up the next priority story |
224
+ | \`intent done <id>\` | Mark a story done + log a decision |
225
+ | \`intent decide\` | Log an architectural or product decision |
226
+ | \`intent example\` | Log a reference pattern for this codebase |
227
+ | \`intent sync\` | Refresh story map + INTENT.md |
228
+ | \`intent sync --context\` | Regenerate this file from intentdocs |
229
+ | \`intent status\` | Show recent activity |
230
+ `;
231
+ if (existsSync2(claudeMdPath)) {
232
+ const existing = readFileSync(claudeMdPath, "utf-8");
233
+ if (!existing.includes("intentdocs Integration")) {
234
+ writeFileSync2(claudeMdPath, existing + "\n" + claudeBlock);
235
+ console.log(" \u2713 Updated CLAUDE.md with intentdocs agent workflow");
236
+ } else {
237
+ console.log(" \u2713 CLAUDE.md already configured");
238
+ }
239
+ } else {
240
+ writeFileSync2(claudeMdPath, claudeBlock.trim() + "\n");
241
+ console.log(" \u2713 Created CLAUDE.md with intentdocs agent workflow");
242
+ }
243
+ const commandsDir = join2(cwd, ".claude", "commands");
244
+ if (!existsSync2(commandsDir)) mkdirSync(commandsDir, { recursive: true });
245
+ const oldPbCommands = ["pb-next", "pb-implement", "pb-review", "pb-document", "pb-test", "pb-status", "pb-done", "pb-sync", "pb-propose", "pb-context"];
246
+ for (const name of oldPbCommands) {
247
+ const oldPath = join2(commandsDir, name + ".md");
248
+ if (existsSync2(oldPath)) {
249
+ unlinkSync(oldPath);
250
+ }
251
+ }
252
+ writeFileSync2(
253
+ join2(commandsDir, "intent-next.md"),
254
+ `# Pick up the next story
255
+
256
+ 1. Run \`intent next\` to get the highest-priority story
257
+ 2. Read the acceptance criteria carefully \u2014 these define "done"
258
+ 3. Check ARCHITECTURE.md for relevant technical decisions
259
+ 4. Read CONVENTIONS.md for coding patterns to follow
260
+ 5. Outline your implementation plan before writing code
261
+ 6. Ask for confirmation before proceeding
262
+ `
263
+ );
264
+ writeFileSync2(
265
+ join2(commandsDir, "intent-implement.md"),
266
+ `# Implement a story
267
+
268
+ ## Context to load first
269
+ 1. Read CLAUDE.md \u2014 your full project harness (instructions, decisions, examples, tools, guardrails)
270
+ 2. Run \`intent next\` if you don't have a story yet
271
+ 3. Review the "Done when" acceptance criteria \u2014 each one carries a priority
272
+ (\`must\`/\`should\`) and a verification method: \`[executable]\` gives you a
273
+ \`check:\` command to run, \`[self-check]\`/\`[visual]\`/\`[founder]\` give you
274
+ a \`check:\` question to answer by inspecting code/output
275
+
276
+ ## Process
277
+ 4. Plan: which files change? Check the Reference Patterns in CLAUDE.md first.
278
+ 5. Implement \u2014 stay inside the Guardrails section
279
+ 6. Write or update tests
280
+
281
+ ## The acceptance-criteria loop
282
+ 7. Evaluate every criterion using its verification method: run the \`check:\`
283
+ command for \`[executable]\` criteria, answer the \`check:\` question for
284
+ the others.
285
+ 8. Report every result via the \`report_criteria\` MCP tool
286
+ (\`{ story_id, results: [{ criterion_id, status: pass|fail|blocked, detail }] }\`).
287
+ 9. For any failing \`must\` criterion: fix it and re-check. If the *same*
288
+ \`must\` criterion fails 3 times, call \`raise_blocker\` with what you
289
+ attempted, what's failing, and the single decision the founder needs to
290
+ make \u2014 then stop working on this story.
291
+ 10. Never call \`mark_done\` (or \`intent done\`) while a \`must\` criterion is
292
+ still failing or has never been reported \u2014 both will warn you and
293
+ require an explicit override if you try.
294
+
295
+ ## Write back to intentdocs
296
+ 11. Add implementation notes:
297
+ \`intent note <story-id> "How this was implemented"\`
298
+ 12. Log decisions: \`intent decide "Title" -d "What and why" -c architecture\`
299
+ 13. Log useful patterns: \`intent example\`
300
+ 14. If you discovered new requirements:
301
+ \`intent add "title" --activity "Activity" --priority should\`
302
+ 15. Mark done: \`intent done <story-id>\`
303
+ 16. Commit: \`git commit -m "feat: description [SC-<id>]"\`
304
+
305
+ ## Rules
306
+ - Don't over-engineer. Build exactly what the acceptance criteria describe.
307
+ - Always log significant decisions \u2014 future developers need context.
308
+ - Follow patterns in CONVENTIONS.md
309
+ - Never mark a story done with a failing or unreported \`must\` criterion.
310
+ `
311
+ );
312
+ writeFileSync2(
313
+ join2(commandsDir, "intent-review.md"),
314
+ `# Review against acceptance criteria
315
+
316
+ 1. Identify the story being worked on
317
+ 2. For each acceptance criterion:
318
+ - Does the implementation satisfy it? (yes/no/partial)
319
+ - If partial, what's missing?
320
+ 3. Check for: edge cases, error handling, accessibility, security
321
+ 4. Summarise: what's done, what needs work, any concerns
322
+ `
323
+ );
324
+ writeFileSync2(
325
+ join2(commandsDir, "intent-document.md"),
326
+ `# Document a feature
327
+
328
+ 1. Check which story was just completed
329
+ 2. Read INTENT.md for product context
330
+ 3. Write a brief markdown doc explaining:
331
+ - What was built and why
332
+ - How it works (architecture-level)
333
+ - Decisions made and trade-offs
334
+ - How to test it manually
335
+ 4. Log decisions to intentdocs:
336
+ \`intent decide "Title" -d "Description" -c architecture\`
337
+ 5. Add implementation notes:
338
+ \`intent note <story-id> "Summary of how it works"\`
339
+ 6. Update ARCHITECTURE.md if needed
340
+ 7. Update CONVENTIONS.md if new patterns were introduced
341
+ `
342
+ );
343
+ writeFileSync2(
344
+ join2(commandsDir, "intent-test.md"),
345
+ `# Write tests for a story
346
+
347
+ 1. Get the story details: \`intent next\` or provided story ID
348
+ 2. Each acceptance criterion becomes at least one test
349
+ 3. Also consider: error cases, boundary conditions, integration points
350
+ 4. Follow testing patterns in CONVENTIONS.md
351
+ 5. Run the tests to verify they pass
352
+ `
353
+ );
354
+ writeFileSync2(
355
+ join2(commandsDir, "intent-status.md"),
356
+ `Run \`intent status\` and \`intent stories --filter todo\` to show current progress and remaining backlog. Summarise what's done and what's left.`
357
+ );
358
+ writeFileSync2(
359
+ join2(commandsDir, "intent-done.md"),
360
+ `Ask which story was just completed, then run \`intent done <id>\`.
361
+ \`intent done\` prints the acceptance-criteria table first. If any \`must\` criterion is still failing or was
362
+ never reported via \`report_criteria\`, it will list them and ask "Mark done anyway?" \u2014 only confirm that
363
+ if the founder explicitly wants to override; otherwise go fix the failing criterion and re-run \`intent done\`.
364
+ The CLI will prompt for an optional decision or lesson learned \u2014 encourage the user to capture it.
365
+ Also ask if there are implementation notes to add: \`intent note <id> "..."\`
366
+ Both decisions and notes flow back to the project page on intentdocs.com.`
367
+ );
368
+ writeFileSync2(
369
+ join2(commandsDir, "intent-sync.md"),
370
+ `# Refresh context from intentdocs
371
+
372
+ ## Refresh story map summary
373
+ Run \`intent sync\` \u2014 updates INTENT.md with the latest stories and their status.
374
+
375
+ ## Regenerate the full CLAUDE.md harness
376
+ Run \`intent sync --context\` \u2014 rebuilds CLAUDE.md from your project page, pulling in:
377
+ - Role & Goals (Instructions section)
378
+ - Scope (all story maps + story counts)
379
+ - Decisions & Memory (last 10 decisions)
380
+ - Reference Patterns (Examples you've logged)
381
+ - Tools & Stack (structured tool registry + MCP config)
382
+ - Guardrails (hard constraints)
383
+
384
+ Run \`intent sync --context\` after updating any section on intentdocs.com.`
385
+ );
386
+ writeFileSync2(
387
+ join2(commandsDir, "intent-propose.md"),
388
+ `# Propose a new change
389
+
390
+ Use this when you want to plan a new feature, improvement, or fix before writing code.
391
+ intentdocs stores the source of truth in your online story map \u2014 this command creates
392
+ stories there AND saves a local spec for the AI to reference during implementation.
393
+
394
+ ## Process
395
+ 1. Ask the user: "What do you want to build or change? Describe the problem and the solution."
396
+ 2. Help them break it into 2-5 stories with clear acceptance criteria (each AC is testable)
397
+ 3. Run: \`intent propose <change-name>\`
398
+ - The CLI will guide through: problem, solution, activity, priority, stories + ACs
399
+ - Stories are created in the live story map at intentdocs.com
400
+ - A local spec is saved to \`intent-changes/<name>/proposal.md\`
401
+ 4. Once done: \`intent sync\` to pull the new stories into INTENT.md
402
+ 5. Run \`intent next\` to start implementing
403
+
404
+ ## Tips
405
+ - Change names should be kebab-case: add-dark-mode, fix-auth-flow, onboarding-revamp
406
+ - Keep stories small \u2014 one story = one deployable increment
407
+ - Write ACs as testable outcomes: "Given X, when Y, then Z" or "User can..."
408
+ - If the change is already defined in the UI, just run \`intent sync\` instead
409
+ `
410
+ );
411
+ writeFileSync2(
412
+ join2(commandsDir, "intent-context.md"),
413
+ `# Load current story context
414
+
415
+ Use this to focus on a single story without loading the full story map.
416
+
417
+ ## When to use
418
+ - At the start of a coding session for a specific story
419
+ - When switching between stories mid-session
420
+ - When you need a clean, focused view of ACs and notes
421
+
422
+ ## Process
423
+ 1. Run \`intent next\` to get the active story \u2014 this automatically writes the context file to \`.intent/stories/<id>.md\`
424
+ 2. Read that file \u2014 it contains: title, user story, acceptance criteria (as checkboxes), notes, and space for relevant files
425
+ 3. Use it as your working document throughout implementation:
426
+ - Check off ACs as you satisfy them
427
+ - Add file paths under "Relevant files" as you discover them
428
+ - Run \`intent note <id> "..."\` to log notes (updates both remote map and local file)
429
+ 4. When all ACs are checked: \`intent done <id>\`
430
+
431
+ ## If you already know the story ID
432
+ The file is at \`.intent/stories/<full-story-id>.md\` \u2014 read it directly.
433
+ `
434
+ );
435
+ writeFileSync2(
436
+ join2(commandsDir, "intent-decide.md"),
437
+ `# Log an architectural or product decision
438
+
439
+ Use this when you've made a decision that future agents (or teammates) should know about.
440
+
441
+ Run \`intent decide\` \u2014 the CLI will prompt for:
442
+ - Title: short, specific description of the decision
443
+ - Description: what was decided and why (include trade-offs considered)
444
+ - Category: architecture / product / technical / trade-off
445
+
446
+ Decisions appear in the Memory section of the project page at intentdocs.com
447
+ and are included in CLAUDE.md the next time you run \`intent sync --context\`.
448
+
449
+ Good decisions to log:
450
+ - "Chose X over Y because..."
451
+ - "Decided not to do Z because..."
452
+ - "This pattern is used throughout because..."`
453
+ );
454
+ writeFileSync2(
455
+ join2(commandsDir, "intent-example.md"),
456
+ `# Log a reference pattern for this codebase
457
+
458
+ Use this when you've written code that represents the right way to do something in this project.
459
+
460
+ Run \`intent example\` \u2014 the CLI will prompt for:
461
+ - Title: what the pattern is
462
+ - Description: when to use it
463
+ - Language: typescript / python / bash / other
464
+ - Before code: the old way (optional)
465
+ - After code: the correct pattern to follow
466
+
467
+ Examples appear in the Knowledge > Examples section of the project page
468
+ and are included in CLAUDE.md as Reference Patterns the next time you run \`intent sync --context\`.`
469
+ );
470
+ console.log(" \u2713 Created slash commands: /intent-propose, /intent-next, /intent-implement, /intent-review, /intent-document, /intent-test, /intent-status, /intent-done, /intent-sync, /intent-context, /intent-decide, /intent-example");
471
+ registerMcpWithClaudeCode();
472
+ }
473
+ function registerMcpWithClaudeCode() {
474
+ const settingsPath = join2(homedir(), ".claude", "settings.json");
475
+ let settings = {};
476
+ if (existsSync2(settingsPath)) {
477
+ try {
478
+ settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
479
+ } catch {
480
+ }
481
+ }
482
+ const servers = settings.mcpServers ?? {};
483
+ servers.intent = { command: "npx", args: ["intentdocs", "mcp"] };
484
+ settings.mcpServers = servers;
485
+ mkdirSync(dirname(settingsPath), { recursive: true });
486
+ writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
487
+ console.log(" \u2713 Registered MCP server in ~/.claude/settings.json \u2014 restart Claude Code to activate");
488
+ }
489
+ function setupCursor(syncToken) {
490
+ const cwd = process.cwd();
491
+ const cursorRulesPath = join2(cwd, ".cursorrules");
492
+ const cursorBlock = `
493
+ # intentdocs Integration
494
+
495
+ ## Context files
496
+ 1. **INTENT.md** \u2014 product story map with personas, stories, acceptance criteria
497
+ 2. **ARCHITECTURE.md** \u2014 technical decisions and system structure
498
+ 3. **CONVENTIONS.md** \u2014 coding patterns, naming, file structure
499
+
500
+ ## Workflow
501
+ - Next story: \`intent next\`
502
+ - Mark done: \`intent done <id>\`
503
+ - Add notes: \`intent note <id> "..."\`
504
+ - Log decisions: \`intent decide "Title" -d "Why"\`
505
+ - Create stories: \`intent add "title" --activity "Name"\`
506
+ - Refresh context: \`intent sync\`
507
+
508
+ ## The acceptance-criteria loop
509
+ - Each "Done when" criterion carries a priority (\`must\`/\`should\`) and a
510
+ verification method: \`[executable]\` gives a \`check:\` command to run;
511
+ \`[self-check]\`/\`[visual]\`/\`[founder]\` give a \`check:\` question to answer.
512
+ - After implementing, evaluate every criterion and report results with the
513
+ \`report_criteria\` MCP tool: \`{ story_id, results: [{ criterion_id, status: pass|fail|blocked, detail }] }\`
514
+ - For a failing \`must\` criterion: fix and re-check. If the same \`must\`
515
+ fails 3 times, call \`raise_blocker\` with what you attempted, what's
516
+ failing, and the single decision needed \u2014 then stop.
517
+ - Never call \`mark_done\`/\`intent done\` while a \`must\` criterion is still
518
+ failing or unreported \u2014 both warn and require an explicit override.
519
+
520
+ ## Rules
521
+ - Read acceptance criteria before implementing
522
+ - Don't over-engineer \u2014 build what's needed
523
+ - Log decisions and trade-offs back to intentdocs
524
+ - Follow patterns in CONVENTIONS.md
525
+ `;
526
+ if (existsSync2(cursorRulesPath)) {
527
+ const existing = readFileSync(cursorRulesPath, "utf-8");
528
+ if (!existing.includes("intentdocs Integration")) {
529
+ writeFileSync2(cursorRulesPath, existing + "\n" + cursorBlock);
530
+ console.log(" \u2713 Updated .cursorrules");
531
+ }
532
+ } else {
533
+ writeFileSync2(cursorRulesPath, cursorBlock.trim() + "\n");
534
+ console.log(" \u2713 Created .cursorrules");
535
+ }
536
+ console.log(`
537
+ To enable MCP tools in Cursor:
538
+
539
+ {
540
+ "mcpServers": {
541
+ "intentdocs": {
542
+ "command": "npx",
543
+ "args": ["intentdocs", "mcp"],
544
+ "cwd": "${cwd}"
545
+ }
546
+ }
547
+ }
548
+ `);
549
+ }
550
+ function generateAgentDocs() {
551
+ const cwd = process.cwd();
552
+ const archPath = join2(cwd, "ARCHITECTURE.md");
553
+ if (!existsSync2(archPath)) {
554
+ writeFileSync2(
555
+ archPath,
556
+ `# Architecture
557
+
558
+ Document technical decisions here. Your AI tool reads this before implementing features.
559
+
560
+ ## Decisions
561
+
562
+ | Date | Decision | Rationale |
563
+ |------|----------|-----------|
564
+ | ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]} | Project initialised with intentdocs | Story-map-driven development |
565
+
566
+ ## Stack
567
+
568
+ _Fill in your tech stack here._
569
+
570
+ ## Patterns
571
+
572
+ _Document recurring patterns._
573
+
574
+ ## Trade-offs
575
+
576
+ _Record trade-offs and why._
577
+ `
578
+ );
579
+ console.log(" \u2713 Created ARCHITECTURE.md scaffold");
580
+ }
581
+ const convPath = join2(cwd, "CONVENTIONS.md");
582
+ if (!existsSync2(convPath)) {
583
+ writeFileSync2(
584
+ convPath,
585
+ `# Conventions
586
+
587
+ Coding rules for this project. Your AI tool follows these when writing code.
588
+
589
+ ## File structure
590
+
591
+ _Describe your preferred file/folder structure._
592
+
593
+ ## Naming
594
+
595
+ _Naming conventions (camelCase, PascalCase, etc.)._
596
+
597
+ ## Testing
598
+
599
+ _Testing patterns._
600
+
601
+ ## Git
602
+
603
+ - Branches: \`feat/<description>\`, \`fix/<description>\`
604
+ - Commits: \`feat: description [INTENT-<story-id>]\`
605
+ - Always create PRs \u2014 never commit directly to main
606
+
607
+ ## Code style
608
+
609
+ _Linting, formatting, style preferences._
610
+ `
611
+ );
612
+ console.log(" \u2713 Created CONVENTIONS.md scaffold");
613
+ }
614
+ }
615
+
616
+ export {
617
+ captureCli,
618
+ shutdownCliAnalytics,
619
+ sync,
620
+ setup
621
+ };
@@ -0,0 +1,21 @@
1
+ // src/lib/upgrade.ts
2
+ import chalk from "chalk";
3
+ function isPlanGated(status) {
4
+ return status === 402 || status === 403;
5
+ }
6
+ var UPGRADE_MESSAGE_PLAIN = "This feature requires a Pro plan or your own Anthropic key.\n Upgrade: https://intentdocs.com/pricing\n Or add your key: intent keys set";
7
+ function printUpgradeMessage() {
8
+ console.log();
9
+ console.log(
10
+ ` ${chalk.yellow("This feature requires a Pro plan or your own Anthropic key.")}`
11
+ );
12
+ console.log(` Upgrade: ${chalk.cyan.underline("https://intentdocs.com/pricing")}`);
13
+ console.log(` Or add your key: ${chalk.cyan("intent keys set")}`);
14
+ console.log();
15
+ }
16
+
17
+ export {
18
+ isPlanGated,
19
+ UPGRADE_MESSAGE_PLAIN,
20
+ printUpgradeMessage
21
+ };
@@ -0,0 +1,34 @@
1
+ // src/lib/auth.ts
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ var AUTH_DIR = join(homedir(), ".intent");
6
+ var AUTH_FILE = join(AUTH_DIR, "auth.json");
7
+ function getAuth() {
8
+ try {
9
+ const raw = readFileSync(AUTH_FILE, "utf-8");
10
+ return JSON.parse(raw);
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+ function saveAuth(data) {
16
+ if (!existsSync(AUTH_DIR)) mkdirSync(AUTH_DIR, { recursive: true });
17
+ writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
18
+ }
19
+ function requireAuth() {
20
+ const auth = getAuth();
21
+ if (!auth) {
22
+ console.error("Not logged in. Run: intent login");
23
+ process.exit(1);
24
+ }
25
+ return auth;
26
+ }
27
+ var API_BASE = "https://intentdocs.com";
28
+
29
+ export {
30
+ getAuth,
31
+ saveAuth,
32
+ requireAuth,
33
+ API_BASE
34
+ };