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.
- package/README.md +261 -0
- package/bin/intent.js +2 -0
- package/dist/chunk-4OWPN2RE.js +249 -0
- package/dist/chunk-6FGYJ3W2.js +140 -0
- package/dist/chunk-6XKB6RTU.js +123 -0
- package/dist/chunk-ICOMMHKG.js +48 -0
- package/dist/chunk-IKF3ZLHK.js +621 -0
- package/dist/chunk-ITQUQYWB.js +21 -0
- package/dist/chunk-PCNDP5SX.js +34 -0
- package/dist/chunk-WPVDURTJ.js +79 -0
- package/dist/complexity-NSSYVT54.js +79 -0
- package/dist/import-prd-CKV3R67G.js +134 -0
- package/dist/index.js +2821 -0
- package/dist/keys-EDWET3WG.js +90 -0
- package/dist/log-TT2UCJP7.js +94 -0
- package/dist/map-extract-I3AACTGL.js +98 -0
- package/dist/map-import-IYUMRASO.js +358 -0
- package/dist/map-schema-JVL3O2TJ.js +114 -0
- package/dist/prompt-WAWCRGHN.js +16 -0
- package/dist/research-6SXJ37TI.js +59 -0
- package/dist/scope-OGKQ472D.js +10 -0
- package/dist/setup-SOI2HQAL.js +11 -0
- package/package.json +43 -0
|
@@ -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
|
+
};
|