@ulysses-ai/create-workspace 0.13.0-beta.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/LICENSE +21 -0
- package/README.md +108 -0
- package/bin/create.mjs +79 -0
- package/lib/git.mjs +26 -0
- package/lib/init.mjs +129 -0
- package/lib/payload.mjs +44 -0
- package/lib/prompts.mjs +113 -0
- package/lib/scaffold.mjs +84 -0
- package/lib/upgrade.mjs +42 -0
- package/package.json +43 -0
- package/template/.claude/agents/aside-researcher.md +48 -0
- package/template/.claude/agents/implementer.md +39 -0
- package/template/.claude/agents/researcher.md +40 -0
- package/template/.claude/agents/reviewer.md +47 -0
- package/template/.claude/hooks/_utils.mjs +196 -0
- package/template/.claude/hooks/_utils.test.mjs +99 -0
- package/template/.claude/hooks/post-compact.mjs +7 -0
- package/template/.claude/hooks/pre-compact.mjs +34 -0
- package/template/.claude/hooks/repo-write-detection.mjs +107 -0
- package/template/.claude/hooks/session-end.mjs +91 -0
- package/template/.claude/hooks/session-start.mjs +150 -0
- package/template/.claude/hooks/subagent-start.mjs +44 -0
- package/template/.claude/hooks/workspace-update-check.mjs +42 -0
- package/template/.claude/hooks/worktree-create.mjs +53 -0
- package/template/.claude/lib/session-frontmatter.mjs +265 -0
- package/template/.claude/lib/session-frontmatter.test.mjs +242 -0
- package/template/.claude/recipes/migrate-from-notion.md +120 -0
- package/template/.claude/rules/agent-rules.md.skip +32 -0
- package/template/.claude/rules/cloud-infrastructure.md.skip +15 -0
- package/template/.claude/rules/coherent-revisions.md +24 -0
- package/template/.claude/rules/documentation.md.skip +13 -0
- package/template/.claude/rules/git-conventions.md +34 -0
- package/template/.claude/rules/honest-pushback.md +56 -0
- package/template/.claude/rules/local-dev-environment.md.skip +60 -0
- package/template/.claude/rules/memory-guidance.md +26 -0
- package/template/.claude/rules/product-integrity.md.skip +24 -0
- package/template/.claude/rules/scope-guard.md.skip +22 -0
- package/template/.claude/rules/superpowers-workflow.md.skip +22 -0
- package/template/.claude/rules/token-economics.md.skip +31 -0
- package/template/.claude/rules/work-item-tracking.md +90 -0
- package/template/.claude/rules/workspace-structure.md +69 -0
- package/template/.claude/scripts/add-repo-to-session.mjs +78 -0
- package/template/.claude/scripts/cleanup-work-session.mjs +108 -0
- package/template/.claude/scripts/create-work-session.mjs +124 -0
- package/template/.claude/scripts/migrate-open-work.mjs +91 -0
- package/template/.claude/scripts/migrate-session-layout.mjs +236 -0
- package/template/.claude/scripts/migrate-session-layout.test.mjs +144 -0
- package/template/.claude/scripts/trackers/github-issues.mjs +170 -0
- package/template/.claude/scripts/trackers/github-issues.test.mjs +190 -0
- package/template/.claude/scripts/trackers/interface.mjs +25 -0
- package/template/.claude/scripts/trackers/interface.test.mjs +40 -0
- package/template/.claude/settings.json +107 -0
- package/template/.claude/skills/aside/SKILL.md +125 -0
- package/template/.claude/skills/braindump/SKILL.md +96 -0
- package/template/.claude/skills/build-docs-site/SKILL.md +323 -0
- package/template/.claude/skills/build-docs-site/checklists/framing.md +221 -0
- package/template/.claude/skills/build-docs-site/checklists/pitfalls.md +228 -0
- package/template/.claude/skills/build-docs-site/checklists/review.md +130 -0
- package/template/.claude/skills/build-docs-site/scripts/bulk-fill-migration.py +393 -0
- package/template/.claude/skills/build-docs-site/scripts/forbidden-word-grep.mjs +159 -0
- package/template/.claude/skills/build-docs-site/scripts/leak-grep.mjs +328 -0
- package/template/.claude/skills/build-docs-site/templates/custom.css.tmpl +212 -0
- package/template/.claude/skills/build-docs-site/templates/docusaurus.config.ts.tmpl +95 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/Arrow.tsx +87 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/Box.tsx +90 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/DiagramContainer.tsx +46 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/Region.tsx +68 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/SectionTitle.tsx +42 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/tokens.ts +67 -0
- package/template/.claude/skills/build-docs-site/templates/sidebars.ts.tmpl +89 -0
- package/template/.claude/skills/build-docs-site/templates/spec.md.tmpl +119 -0
- package/template/.claude/skills/complete-work/SKILL.md +369 -0
- package/template/.claude/skills/handoff/SKILL.md +98 -0
- package/template/.claude/skills/maintenance/SKILL.md +116 -0
- package/template/.claude/skills/pause-work/SKILL.md +98 -0
- package/template/.claude/skills/promote/SKILL.md +77 -0
- package/template/.claude/skills/release/SKILL.md +126 -0
- package/template/.claude/skills/setup-tracker/SKILL.md +117 -0
- package/template/.claude/skills/start-work/SKILL.md +234 -0
- package/template/.claude/skills/sync-work/SKILL.md +73 -0
- package/template/.claude/skills/workspace-init/SKILL.md +420 -0
- package/template/.claude/skills/workspace-update/SKILL.md +108 -0
- package/template/.mcp.json +12 -0
- package/template/CLAUDE.md.tmpl +32 -0
- package/template/_gitignore +28 -0
- package/template/workspace.json.tmpl +15 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Unit tests for session-frontmatter.mjs
|
|
3
|
+
// Run: node template/.claude/lib/session-frontmatter.test.mjs
|
|
4
|
+
import {
|
|
5
|
+
parseSessionContent,
|
|
6
|
+
updateSessionContent,
|
|
7
|
+
} from './session-frontmatter.mjs';
|
|
8
|
+
|
|
9
|
+
let failed = 0;
|
|
10
|
+
let passed = 0;
|
|
11
|
+
|
|
12
|
+
function assert(cond, msg) {
|
|
13
|
+
if (cond) {
|
|
14
|
+
passed++;
|
|
15
|
+
} else {
|
|
16
|
+
failed++;
|
|
17
|
+
console.error(` FAIL: ${msg}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function assertEq(actual, expected, msg) {
|
|
22
|
+
const a = JSON.stringify(actual);
|
|
23
|
+
const e = JSON.stringify(expected);
|
|
24
|
+
if (a === e) {
|
|
25
|
+
passed++;
|
|
26
|
+
} else {
|
|
27
|
+
failed++;
|
|
28
|
+
console.error(` FAIL: ${msg}\n expected: ${e}\n actual: ${a}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const SAMPLE = `---
|
|
33
|
+
type: session-tracker
|
|
34
|
+
name: fix-auth
|
|
35
|
+
description: Fix the auth timeout on mobile
|
|
36
|
+
status: active
|
|
37
|
+
branch: bugfix/fix-auth
|
|
38
|
+
created: 2026-04-13T05:33:50.000Z
|
|
39
|
+
user: alice
|
|
40
|
+
repos:
|
|
41
|
+
- my-app
|
|
42
|
+
- my-api
|
|
43
|
+
workItem: 3
|
|
44
|
+
chatSessions:
|
|
45
|
+
- id: aa3c952e-dbff-4055-8bcc-e5f217618d57
|
|
46
|
+
names: [pickup-from-braindump]
|
|
47
|
+
started: 2026-04-13T05:33:50.000Z
|
|
48
|
+
ended: 2026-04-13T07:12:00.000Z
|
|
49
|
+
- id: bb4d063e-ec00-4166-cc71-cd3d3d4628a1
|
|
50
|
+
names: []
|
|
51
|
+
started: 2026-04-13T08:00:00.000Z
|
|
52
|
+
ended: null
|
|
53
|
+
author: alice
|
|
54
|
+
updated: 2026-04-13
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
# Work Session: fix-auth
|
|
58
|
+
|
|
59
|
+
Brief one-liner.
|
|
60
|
+
|
|
61
|
+
## Progress
|
|
62
|
+
|
|
63
|
+
Some prose.
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
// === Test 1: parse all field types ===
|
|
67
|
+
console.log('Test 1: parse all field types');
|
|
68
|
+
{
|
|
69
|
+
const parsed = parseSessionContent(SAMPLE);
|
|
70
|
+
assertEq(parsed.fields.type, 'session-tracker', 'type');
|
|
71
|
+
assertEq(parsed.fields.name, 'fix-auth', 'name');
|
|
72
|
+
assertEq(parsed.fields.description, 'Fix the auth timeout on mobile', 'description');
|
|
73
|
+
assertEq(parsed.fields.status, 'active', 'status');
|
|
74
|
+
assertEq(parsed.fields.branch, 'bugfix/fix-auth', 'branch');
|
|
75
|
+
assertEq(parsed.fields.created, '2026-04-13T05:33:50.000Z', 'created (ISO timestamp unquoted)');
|
|
76
|
+
assertEq(parsed.fields.workItem, 3, 'workItem (integer)');
|
|
77
|
+
assertEq(parsed.fields.repos, ['my-app', 'my-api'], 'repos (flat list)');
|
|
78
|
+
assertEq(parsed.fields.chatSessions.length, 2, 'chatSessions length');
|
|
79
|
+
assertEq(parsed.fields.chatSessions[0].id, 'aa3c952e-dbff-4055-8bcc-e5f217618d57', 'chat 0 id');
|
|
80
|
+
assertEq(parsed.fields.chatSessions[0].names, ['pickup-from-braindump'], 'chat 0 names');
|
|
81
|
+
assertEq(parsed.fields.chatSessions[0].started, '2026-04-13T05:33:50.000Z', 'chat 0 started');
|
|
82
|
+
assertEq(parsed.fields.chatSessions[0].ended, '2026-04-13T07:12:00.000Z', 'chat 0 ended');
|
|
83
|
+
assertEq(parsed.fields.chatSessions[1].id, 'bb4d063e-ec00-4166-cc71-cd3d3d4628a1', 'chat 1 id');
|
|
84
|
+
assertEq(parsed.fields.chatSessions[1].names, [], 'chat 1 names (empty inline list)');
|
|
85
|
+
assertEq(parsed.fields.chatSessions[1].ended, null, 'chat 1 ended (null)');
|
|
86
|
+
assert(parsed.body.startsWith('\n# Work Session'), 'body starts with blank line + H1');
|
|
87
|
+
assert(parsed.body.endsWith('Some prose.\n'), 'body ends with trailing newline');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// === Test 2: lossless byte-identity on no-op update ===
|
|
91
|
+
console.log('Test 2: lossless byte-identity on no-op update');
|
|
92
|
+
{
|
|
93
|
+
const unchanged = updateSessionContent(SAMPLE, {});
|
|
94
|
+
assertEq(unchanged, SAMPLE, 'no-op update preserves content byte-identical');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// === Test 3: update single scalar field ===
|
|
98
|
+
console.log('Test 3: update single scalar field');
|
|
99
|
+
{
|
|
100
|
+
const updated = updateSessionContent(SAMPLE, { status: 'paused' });
|
|
101
|
+
const parsed = parseSessionContent(updated);
|
|
102
|
+
assertEq(parsed.fields.status, 'paused', 'status changed');
|
|
103
|
+
assertEq(parsed.fields.name, 'fix-auth', 'name preserved');
|
|
104
|
+
assertEq(parsed.fields.repos, ['my-app', 'my-api'], 'repos preserved');
|
|
105
|
+
assertEq(parsed.fields.chatSessions.length, 2, 'chatSessions preserved');
|
|
106
|
+
// Verify every other line is byte-identical
|
|
107
|
+
const origLines = SAMPLE.split('\n');
|
|
108
|
+
const newLines = updated.split('\n');
|
|
109
|
+
assertEq(newLines.length, origLines.length, 'same number of lines');
|
|
110
|
+
for (let i = 0; i < origLines.length; i++) {
|
|
111
|
+
if (origLines[i].startsWith('status:')) {
|
|
112
|
+
assertEq(newLines[i], 'status: paused', `line ${i} is new status`);
|
|
113
|
+
} else {
|
|
114
|
+
assertEq(newLines[i], origLines[i], `line ${i} preserved`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// === Test 4: update chatSessions array (append a chat) ===
|
|
120
|
+
console.log('Test 4: update chatSessions array');
|
|
121
|
+
{
|
|
122
|
+
const parsed = parseSessionContent(SAMPLE);
|
|
123
|
+
const newChats = [...parsed.fields.chatSessions, {
|
|
124
|
+
id: 'cc5e174f-fd11-5277-dd82-de4e4e5739b2',
|
|
125
|
+
names: [],
|
|
126
|
+
started: '2026-04-13T09:00:00.000Z',
|
|
127
|
+
ended: null,
|
|
128
|
+
}];
|
|
129
|
+
const updated = updateSessionContent(SAMPLE, { chatSessions: newChats });
|
|
130
|
+
const reparsed = parseSessionContent(updated);
|
|
131
|
+
assertEq(reparsed.fields.chatSessions.length, 3, 'chatSessions now has 3 entries');
|
|
132
|
+
assertEq(reparsed.fields.chatSessions[2].id, 'cc5e174f-fd11-5277-dd82-de4e4e5739b2', 'new chat id');
|
|
133
|
+
assertEq(reparsed.fields.name, 'fix-auth', 'other fields preserved');
|
|
134
|
+
assertEq(reparsed.body, parsed.body, 'body preserved byte-identical');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// === Test 5: append a new field ===
|
|
138
|
+
console.log('Test 5: append a new field');
|
|
139
|
+
{
|
|
140
|
+
const updated = updateSessionContent(SAMPLE, { newField: 'hello' });
|
|
141
|
+
const parsed = parseSessionContent(updated);
|
|
142
|
+
assertEq(parsed.fields.newField, 'hello', 'newField present');
|
|
143
|
+
assertEq(parsed.fields.name, 'fix-auth', 'existing field preserved');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// === Test 6: remove a field ===
|
|
147
|
+
console.log('Test 6: remove a field');
|
|
148
|
+
{
|
|
149
|
+
const updated = updateSessionContent(SAMPLE, { workItem: undefined });
|
|
150
|
+
const parsed = parseSessionContent(updated);
|
|
151
|
+
assertEq(parsed.fields.workItem, undefined, 'workItem removed');
|
|
152
|
+
assertEq(parsed.fields.user, 'alice', 'user preserved');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// === Test 7: update repos (flat list) ===
|
|
156
|
+
console.log('Test 7: update repos flat list');
|
|
157
|
+
{
|
|
158
|
+
const updated = updateSessionContent(SAMPLE, { repos: ['my-app', 'my-api', 'my-web'] });
|
|
159
|
+
const parsed = parseSessionContent(updated);
|
|
160
|
+
assertEq(parsed.fields.repos, ['my-app', 'my-api', 'my-web'], 'repos has 3 items');
|
|
161
|
+
assertEq(parsed.fields.chatSessions.length, 2, 'chatSessions preserved after repos update');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// === Test 8: empty repos ===
|
|
165
|
+
console.log('Test 8: empty repos');
|
|
166
|
+
{
|
|
167
|
+
const updated = updateSessionContent(SAMPLE, { repos: [] });
|
|
168
|
+
const parsed = parseSessionContent(updated);
|
|
169
|
+
assertEq(parsed.fields.repos, [], 'repos is empty');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// === Test 9: scalar with special chars (colon in branch name) ===
|
|
173
|
+
console.log('Test 9: scalar with special chars');
|
|
174
|
+
{
|
|
175
|
+
// Branch names with slashes should NOT be quoted
|
|
176
|
+
const sample = `---
|
|
177
|
+
branch: feature/worksessions-refactor
|
|
178
|
+
---
|
|
179
|
+
body
|
|
180
|
+
`;
|
|
181
|
+
const parsed = parseSessionContent(sample);
|
|
182
|
+
assertEq(parsed.fields.branch, 'feature/worksessions-refactor', 'branch unquoted');
|
|
183
|
+
const noop = updateSessionContent(sample, {});
|
|
184
|
+
assertEq(noop, sample, 'no-op preserves unquoted slash');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// === Test 10: ISO timestamp round-trip ===
|
|
188
|
+
console.log('Test 10: ISO timestamp round-trip');
|
|
189
|
+
{
|
|
190
|
+
const sample = `---
|
|
191
|
+
created: 2026-04-13T05:33:50.000Z
|
|
192
|
+
---
|
|
193
|
+
body
|
|
194
|
+
`;
|
|
195
|
+
const parsed = parseSessionContent(sample);
|
|
196
|
+
assertEq(parsed.fields.created, '2026-04-13T05:33:50.000Z', 'ISO timestamp parsed');
|
|
197
|
+
const updated = updateSessionContent(sample, { created: '2026-05-01T12:00:00.000Z' });
|
|
198
|
+
const reparsed = parseSessionContent(updated);
|
|
199
|
+
assertEq(reparsed.fields.created, '2026-05-01T12:00:00.000Z', 'ISO timestamp round-trip');
|
|
200
|
+
// Must not be quoted
|
|
201
|
+
assert(!updated.includes('"2026-05-01'), 'ISO timestamp not quoted on write');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// === Test 11: string that truly needs quoting ===
|
|
205
|
+
console.log('Test 11: string that truly needs quoting');
|
|
206
|
+
{
|
|
207
|
+
const sample = `---
|
|
208
|
+
title: hello
|
|
209
|
+
---
|
|
210
|
+
body
|
|
211
|
+
`;
|
|
212
|
+
const updated = updateSessionContent(sample, { title: 'hello: world' });
|
|
213
|
+
assert(updated.includes('title: "hello: world"'), 'colon-space triggers quoting');
|
|
214
|
+
const reparsed = parseSessionContent(updated);
|
|
215
|
+
assertEq(reparsed.fields.title, 'hello: world', 'quoted value round-trips');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// === Test 12: body preservation with multiline content ===
|
|
219
|
+
console.log('Test 12: body preservation with multiline content');
|
|
220
|
+
{
|
|
221
|
+
const sample = `---
|
|
222
|
+
name: test
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
# Heading
|
|
226
|
+
|
|
227
|
+
Paragraph with multiple lines.
|
|
228
|
+
And more lines.
|
|
229
|
+
|
|
230
|
+
- bullet 1
|
|
231
|
+
- bullet 2
|
|
232
|
+
`;
|
|
233
|
+
const noop = updateSessionContent(sample, {});
|
|
234
|
+
assertEq(noop, sample, 'multiline body preserved byte-identical');
|
|
235
|
+
const updated = updateSessionContent(sample, { name: 'changed' });
|
|
236
|
+
const parsed = parseSessionContent(updated);
|
|
237
|
+
assertEq(parsed.body.trim(), '# Heading\n\nParagraph with multiple lines.\nAnd more lines.\n\n- bullet 1\n- bullet 2', 'body intact after update');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// === Summary ===
|
|
241
|
+
console.log(`\n${passed} passed, ${failed} failed`);
|
|
242
|
+
if (failed > 0) process.exit(1);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Recipe: Migrate from Notion
|
|
2
|
+
|
|
3
|
+
Migrate rules, memory, and handoffs from Notion pages into the claude-workspace template structure.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- The workspace must have a working Notion MCP server configured in `.mcp.json`
|
|
8
|
+
- Run this recipe BEFORE removing the MCP server
|
|
9
|
+
- The scaffolder (`npx @ulysses-ai/create-workspace --init`) should have already been run to install the template structure
|
|
10
|
+
|
|
11
|
+
## Detection
|
|
12
|
+
|
|
13
|
+
This recipe applies when:
|
|
14
|
+
- `.mcp.json` contains a Notion MCP server configuration
|
|
15
|
+
- `CLAUDE.md` (or `.bak`) references Notion page IDs for rules, memory, or handoffs
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
### 1. Identify Notion pages
|
|
20
|
+
|
|
21
|
+
Read `CLAUDE.md.bak` (the pre-migration backup) to find Notion page references:
|
|
22
|
+
- Look for page IDs (32-character hex strings)
|
|
23
|
+
- Note the purpose of each page (rules, memory/context, handoffs)
|
|
24
|
+
|
|
25
|
+
### 2. Extract rules
|
|
26
|
+
|
|
27
|
+
For each Notion page that contains rules/conventions:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Fetch the page content using the Notion MCP server.
|
|
31
|
+
For each rule or convention section:
|
|
32
|
+
- Create a .claude/rules/{rule-name}.md file
|
|
33
|
+
- Use the section heading as the filename (kebab-case)
|
|
34
|
+
- If the rule is project-specific, make it mandatory (.md)
|
|
35
|
+
- If the rule is optional, use .md.skip
|
|
36
|
+
- Write the content as a coherent rule following the template format:
|
|
37
|
+
# Rule Name
|
|
38
|
+
{What the rule says}
|
|
39
|
+
## Why
|
|
40
|
+
{Rationale if provided}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 3. Extract memory and context
|
|
44
|
+
|
|
45
|
+
For each Notion page that contains project memory, context, or key decisions:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Fetch the page content using the Notion MCP server.
|
|
49
|
+
Determine scope:
|
|
50
|
+
- Architecture decisions, tech stack, stable conventions → shared-context/locked/
|
|
51
|
+
- Active project state, current priorities → shared-context/{user}/
|
|
52
|
+
- Historical context no longer relevant → skip
|
|
53
|
+
|
|
54
|
+
For each section:
|
|
55
|
+
- Create a shared-context file with proper frontmatter:
|
|
56
|
+
---
|
|
57
|
+
state: locked (or ephemeral)
|
|
58
|
+
lifecycle: active
|
|
59
|
+
type: promoted
|
|
60
|
+
topic: {section-topic}
|
|
61
|
+
author: {user}
|
|
62
|
+
updated: {today}
|
|
63
|
+
---
|
|
64
|
+
- Write content adapted for the template format
|
|
65
|
+
- One topic per file — split large Notion pages into multiple files
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 4. Extract handoffs
|
|
69
|
+
|
|
70
|
+
For each Notion page that contains session handoffs or context transfers:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
Fetch the page content using the Notion MCP server.
|
|
74
|
+
For recent/active handoffs only (skip stale ones):
|
|
75
|
+
- Create shared-context/{user}/{handoff-name}.md
|
|
76
|
+
- Use handoff frontmatter format with type: handoff
|
|
77
|
+
- Extract: status, key decisions, next steps, open questions
|
|
78
|
+
- Set lifecycle: active (or paused if the work is suspended)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 5. Preserve local preferences
|
|
82
|
+
|
|
83
|
+
Check `CLAUDE.md.bak` for local preferences that aren't Notion-dependent:
|
|
84
|
+
- Repo paths, coding conventions, project-specific notes
|
|
85
|
+
- Add these to appropriate places:
|
|
86
|
+
- Coding conventions → `.claude/rules/` (new rule file)
|
|
87
|
+
- Project notes → `shared-context/locked/` or `shared-context/{user}/`
|
|
88
|
+
- Repo paths → already in `workspace.json`
|
|
89
|
+
|
|
90
|
+
### 6. Remove Notion dependency
|
|
91
|
+
|
|
92
|
+
After all content is extracted and verified:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Back up and remove MCP config
|
|
96
|
+
mv .mcp.json .mcp.json.bak
|
|
97
|
+
|
|
98
|
+
# Remove CLAUDE.md backup (original Notion-fetching version)
|
|
99
|
+
rm CLAUDE.md.bak
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Verify the workspace works without Notion:
|
|
103
|
+
- Start a new Claude Code session
|
|
104
|
+
- Confirm SessionStart hook surfaces the migrated context
|
|
105
|
+
- Confirm rules are loaded (check /context)
|
|
106
|
+
- Confirm skills work
|
|
107
|
+
|
|
108
|
+
### 7. Commit
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
git add .claude/rules/ shared-context/
|
|
112
|
+
git commit -m "chore: migrate Notion content to local files"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Notes
|
|
116
|
+
|
|
117
|
+
- Don't try to migrate everything — only active, relevant content
|
|
118
|
+
- Stale Notion handoffs can be skipped. If it's more than a few weeks old and not referenced, it's dead.
|
|
119
|
+
- Large Notion pages should be split into multiple focused files (one topic per file)
|
|
120
|
+
- The migration is one-way — once verified, the Notion pages become the archive, not the source of truth
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Agent Rules
|
|
2
|
+
|
|
3
|
+
Activate this rule to enforce conventions specifically for subagent behavior.
|
|
4
|
+
|
|
5
|
+
## All Agents
|
|
6
|
+
|
|
7
|
+
- Follow the coherent-revisions rule — rewrite sections from scratch, never inject
|
|
8
|
+
- Report status using the standard protocol: DONE / DONE_WITH_CONCERNS / BLOCKED / NEEDS_CONTEXT
|
|
9
|
+
- If anything is ambiguous, ask before proceeding — don't guess
|
|
10
|
+
- Stay within the scope of the task description — don't explore beyond what was asked
|
|
11
|
+
|
|
12
|
+
## Implementer Agents
|
|
13
|
+
|
|
14
|
+
- Receive full task text in the prompt — never read plan files directly
|
|
15
|
+
- Keep changes minimal and focused on the assigned task
|
|
16
|
+
- Don't restructure code outside the task scope
|
|
17
|
+
- Commit frequently with conventional commit messages
|
|
18
|
+
- It is always OK to say "this is too complex for a single task"
|
|
19
|
+
|
|
20
|
+
## Reviewer Agents
|
|
21
|
+
|
|
22
|
+
- Review only what changed — don't comment on pre-existing code
|
|
23
|
+
- Don't suggest improvements outside the diff scope
|
|
24
|
+
- Be specific: file, line, what's wrong, what to do instead
|
|
25
|
+
- Style preferences defer to linters/formatters — don't enforce personal taste
|
|
26
|
+
|
|
27
|
+
## Researcher Agents
|
|
28
|
+
|
|
29
|
+
- Never modify files — read-only operations only
|
|
30
|
+
- Search thoroughly before reporting "not found"
|
|
31
|
+
- Report with exact file paths and line numbers
|
|
32
|
+
- Flag contradictions between code and documentation
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Cloud Infrastructure
|
|
2
|
+
|
|
3
|
+
Activate this rule if the project uses cloud resources (AWS, GCP, Azure, etc.).
|
|
4
|
+
|
|
5
|
+
## Code-First Rule
|
|
6
|
+
|
|
7
|
+
- All changes to cloud resources go through code and CI/CD
|
|
8
|
+
- Never recommend or execute direct cloud provider changes — no console edits, no CLI commands against live resources
|
|
9
|
+
- If the user asks to do something directly in the cloud provider, warn about drift and recommend the code-first path
|
|
10
|
+
|
|
11
|
+
## Environment Portability
|
|
12
|
+
|
|
13
|
+
- Never hardcode account IDs, resource ARNs/URIs, bucket names, distribution IDs, or environment-specific values
|
|
14
|
+
- All environment-specific values come from environment config files or runtime CI/CD variables
|
|
15
|
+
- This applies to infrastructure code, configuration files, workflow files, and function code
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Coherent Revisions
|
|
2
|
+
|
|
3
|
+
When revising any document or code, rewrite the affected section from start to finish so the result reads as a single coherent piece. Never patch, inject, or insert new content between existing paragraphs/blocks in a way that creates a stitched-together feel.
|
|
4
|
+
|
|
5
|
+
This applies to all written output:
|
|
6
|
+
- Project documentation
|
|
7
|
+
- Specs and design docs
|
|
8
|
+
- Code and logic changes including comments
|
|
9
|
+
- Shared context documents (handoffs, braindumps, locked context)
|
|
10
|
+
- Release notes
|
|
11
|
+
- Open questions
|
|
12
|
+
- Commit messages
|
|
13
|
+
|
|
14
|
+
## Why
|
|
15
|
+
|
|
16
|
+
Injected revisions create fragmented, hard-to-follow output where the seams between old and new content are visible. Rewriting from start to finish produces output that flows naturally and reads as if written in one pass.
|
|
17
|
+
|
|
18
|
+
## In Practice
|
|
19
|
+
|
|
20
|
+
- When updating a section of a document, rewrite the entire section — not just the changed sentences
|
|
21
|
+
- When updating a shared-context file, rewrite it as a fresh snapshot of current understanding
|
|
22
|
+
- When synthesizing multiple sources into release notes, write the narrative from scratch — don't concatenate
|
|
23
|
+
- When revising code with comments, ensure the comments tell a coherent story, not a changelog
|
|
24
|
+
- Small, isolated edits (fixing a typo, updating a single value) are fine — this rule targets substantive revisions
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Documentation
|
|
2
|
+
|
|
3
|
+
Activate this rule if the project maintains documentation that must stay in sync with code.
|
|
4
|
+
|
|
5
|
+
## Doc-Code Consistency
|
|
6
|
+
|
|
7
|
+
- When a code change fixes a bug, corrects an API, or replaces a deprecated pattern, check whether any documentation references the old behavior
|
|
8
|
+
- If it does, update the documentation in the same session — code and docs must never contradict each other
|
|
9
|
+
|
|
10
|
+
## No Unsolicited Docs
|
|
11
|
+
|
|
12
|
+
- Do not create documentation files (README.md, CONTRIBUTING.md, etc.) unless explicitly asked
|
|
13
|
+
- Never create documentation files (*.md) unless the user requests it
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Git Conventions
|
|
2
|
+
|
|
3
|
+
## Branching
|
|
4
|
+
|
|
5
|
+
- Prefixes: `feature/`, `bugfix/`, `chore/`
|
|
6
|
+
- Names: kebab-case after prefix, no grouping/nesting
|
|
7
|
+
- Examples: `feature/ble-provisioning`, `bugfix/mqtt-reconnect`
|
|
8
|
+
- All branches merge to the repo's default branch
|
|
9
|
+
- Branch names should be unique — if revisiting previous work, distinguish the new branch name
|
|
10
|
+
|
|
11
|
+
## Worktrees
|
|
12
|
+
|
|
13
|
+
- Work sessions get N+1 worktrees: one for the workspace, plus one per project repo
|
|
14
|
+
- Each session lives in a self-contained folder at `work-sessions/{session-name}/`
|
|
15
|
+
- The workspace worktree is at `work-sessions/{session-name}/workspace/`
|
|
16
|
+
- Project worktrees are nested inside the workspace worktree at `work-sessions/{session-name}/workspace/repos/{repo-name}/`
|
|
17
|
+
- Example: for a session `fix-auth` on branch `bugfix/fix-auth` touching repos `my-app` and `my-api`:
|
|
18
|
+
- `work-sessions/fix-auth/workspace/` — workspace worktree
|
|
19
|
+
- `work-sessions/fix-auth/workspace/repos/my-app/` — project worktree
|
|
20
|
+
- `work-sessions/fix-auth/workspace/repos/my-api/` — project worktree
|
|
21
|
+
- The workspace repo's `.gitignore` pattern `repos` (no trailing slash) covers both the workspace root's `repos/` and the nested `repos/` inside every worktree
|
|
22
|
+
- Source clones at `repos/{repo-name}/` (at the workspace root) stay on their default branch at all times
|
|
23
|
+
- Remove worktrees when the work session is completed — use the cleanup helper to enforce the mandatory teardown order (project worktrees first, then workspace worktree, then prune)
|
|
24
|
+
|
|
25
|
+
## Branch Maintenance
|
|
26
|
+
|
|
27
|
+
- Before creating a PR, fetch and rebase onto the latest parent branch
|
|
28
|
+
- If conflicts arise during rebase, stop and present them to the user — do not auto-resolve
|
|
29
|
+
|
|
30
|
+
## Commits
|
|
31
|
+
|
|
32
|
+
- Conventional commit format: `feat:`, `fix:`, `refactor:`, `chore:`, `docs:`
|
|
33
|
+
- Never amend commits unless explicitly asked
|
|
34
|
+
- Never force push unless explicitly asked
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Honest Pushback
|
|
2
|
+
|
|
3
|
+
Do not agree with the user just to be agreeable. Do not keep trying things that aren't working. Do not assume when you can verify. Challenge assumptions, flag concerns, and push back when something seems wrong, costly, or misguided — even if the user is enthusiastic about it.
|
|
4
|
+
|
|
5
|
+
## What This Means
|
|
6
|
+
|
|
7
|
+
- If an approach has obvious downsides, say so before implementing
|
|
8
|
+
- If a design decision contradicts an earlier one, flag the contradiction
|
|
9
|
+
- If scope is creeping, name it: "This started as X but is becoming Y. Split?"
|
|
10
|
+
- If you don't know something, say so — don't fabricate confidence
|
|
11
|
+
- If the user's idea is good, a simple "that works" is enough — don't embellish with praise
|
|
12
|
+
- If you made a mistake, own it plainly — don't bury it in hedging language
|
|
13
|
+
|
|
14
|
+
## No Retry Loops
|
|
15
|
+
|
|
16
|
+
When a fix attempt fails, do not immediately try a variation of the same approach. If you have tried a solution and it produced the same error or unexpected result twice, stop and:
|
|
17
|
+
|
|
18
|
+
1. **State what you expected vs what happened.** Be specific — not "it didn't work" but "expected 200, got 403 with message X."
|
|
19
|
+
2. **Identify what you don't understand.** What assumption is failing? Why is the result surprising?
|
|
20
|
+
3. **Research the specific issue.** Read documentation, search for the error message, check source code. Use web search if local sources don't explain it.
|
|
21
|
+
4. **Present your findings.** Tell the user what you learned and what you now think the actual cause is. Propose a solution based on understanding, not guessing.
|
|
22
|
+
|
|
23
|
+
This prevents cycling through variations of the same broken approach, wasting tokens on trial-and-error when reading the docs would take one turn, and the user having to say "stop and actually research this."
|
|
24
|
+
|
|
25
|
+
## Verify, Don't Assume
|
|
26
|
+
|
|
27
|
+
When evidence is available to confirm or deny an assumption, check it before proceeding. Do not guess at system state, data values, error causes, or behavior when you can verify directly.
|
|
28
|
+
|
|
29
|
+
Sources to check before assuming:
|
|
30
|
+
- **Logs** — application logs, server logs, build output. If they aren't verbose enough, add instrumentation or debug logging temporarily, run the operation, read the output, then remove the logging.
|
|
31
|
+
- **Database** — query the actual data instead of assuming what's there.
|
|
32
|
+
- **UI/browser** — test the actual behavior instead of predicting what the user will see. Use browser tools, take screenshots, inspect network requests.
|
|
33
|
+
- **Runtime state** — add a console.log, print statement, or debugger breakpoint. Run it. Read the output.
|
|
34
|
+
- **API responses** — make the actual call instead of assuming the response shape.
|
|
35
|
+
|
|
36
|
+
**Before checking, ask the user:** "I want to verify {what} by {how}. Should I go ahead, or do you want me to just check without asking each time?"
|
|
37
|
+
|
|
38
|
+
If the user says to just check: remember this preference and verify proactively for the rest of the session without asking. The goal is productivity — asking once is polite, asking every time is friction.
|
|
39
|
+
|
|
40
|
+
**When this applies:**
|
|
41
|
+
- You're about to say "I think the issue is..." when you could check
|
|
42
|
+
- You're reasoning about what a function returns when you could call it
|
|
43
|
+
- You're guessing at database state when you could query it
|
|
44
|
+
- You're predicting UI behavior when you could test it
|
|
45
|
+
- You catch yourself writing "probably" or "likely" about something verifiable
|
|
46
|
+
|
|
47
|
+
## What This Does NOT Mean
|
|
48
|
+
|
|
49
|
+
- Don't be contrarian for the sake of it — push back when there's substance, not as a personality trait
|
|
50
|
+
- Don't refuse to execute — voice the concern, then follow the user's decision
|
|
51
|
+
- Don't lecture — state the issue once, clearly, and move on
|
|
52
|
+
- Don't over-verify trivial things — use judgment about what's worth checking
|
|
53
|
+
|
|
54
|
+
## Why
|
|
55
|
+
|
|
56
|
+
Sycophantic AI wastes time, erodes trust, and lets bad decisions through unchallenged. Retry loops burn tokens and frustrate everyone. Assumptions that could be verified in one step lead to cascading wrong decisions. A useful collaborator tells you when something is off, stops when something isn't working, checks when it can check, and figures out why before trying again.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Local Dev Environment
|
|
2
|
+
|
|
3
|
+
Maintain a local-only document at `shared-context/locked/local-only-dev-environment.md` that captures machine-specific development context. This file is gitignored (local-only prefix) but positioned in locked for maximum visibility — Claude sees it every turn.
|
|
4
|
+
|
|
5
|
+
## What to Record
|
|
6
|
+
|
|
7
|
+
When you discover any of the following during work, add it to the local dev environment doc:
|
|
8
|
+
|
|
9
|
+
- **Services & ports** — dev server URLs, database ports, API base URLs, queue endpoints
|
|
10
|
+
- **Credentials** — local dev passwords, API keys for dev/staging, test user accounts, tokens
|
|
11
|
+
- **CLI commands** — project-specific build/run/test commands, Docker invocations, migration commands
|
|
12
|
+
- **Environment variables** — required env vars and their local values, .env file locations
|
|
13
|
+
- **Paths** — local SDK paths, tool installations, config file locations, certificate paths
|
|
14
|
+
- **Infrastructure** — local Docker containers, dev database names, S3 bucket names for dev
|
|
15
|
+
|
|
16
|
+
## How to Record
|
|
17
|
+
|
|
18
|
+
When you encounter a local development detail worth preserving:
|
|
19
|
+
|
|
20
|
+
1. Check if `shared-context/locked/local-only-dev-environment.md` exists
|
|
21
|
+
2. If not, create it with this structure:
|
|
22
|
+
```markdown
|
|
23
|
+
---
|
|
24
|
+
state: locked
|
|
25
|
+
type: reference
|
|
26
|
+
topic: local-dev-environment
|
|
27
|
+
updated: {today}
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Local Dev Environment
|
|
31
|
+
|
|
32
|
+
Machine-specific development context. This file is gitignored — never shared.
|
|
33
|
+
|
|
34
|
+
## Services & Ports
|
|
35
|
+
|
|
36
|
+
## Credentials
|
|
37
|
+
|
|
38
|
+
## CLI Commands
|
|
39
|
+
|
|
40
|
+
## Environment Variables
|
|
41
|
+
|
|
42
|
+
## Paths
|
|
43
|
+
|
|
44
|
+
## Infrastructure
|
|
45
|
+
```
|
|
46
|
+
3. Add the new detail under the appropriate section
|
|
47
|
+
4. Update the `updated:` date in frontmatter
|
|
48
|
+
|
|
49
|
+
## When to Ask
|
|
50
|
+
|
|
51
|
+
- **Credentials:** Always ask before recording: "Save this credential to your local dev doc? (gitignored, never shared)" — even though it's local-only, confirm the user wants it persisted.
|
|
52
|
+
- **Everything else:** Record proactively when discovered. No need to ask — if you see the dev server is on port 3000, just add it.
|
|
53
|
+
|
|
54
|
+
## When This Applies
|
|
55
|
+
|
|
56
|
+
- You run a command and see a port or URL in the output
|
|
57
|
+
- The user mentions "my API key is..." or "the database is at..."
|
|
58
|
+
- You read a .env file or docker-compose.yml and learn the local setup
|
|
59
|
+
- You troubleshoot a connection issue and discover the correct endpoint
|
|
60
|
+
- The user provides credentials for a dev service
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Memory Guidance
|
|
2
|
+
|
|
3
|
+
Guide Claude's auto-memory system for this workspace.
|
|
4
|
+
|
|
5
|
+
## What to Auto-Remember
|
|
6
|
+
|
|
7
|
+
When working in this workspace, pay attention to and save memories about:
|
|
8
|
+
- Architecture decisions and their rationale
|
|
9
|
+
- Patterns that caused bugs or confusion
|
|
10
|
+
- User corrections about project conventions
|
|
11
|
+
- External system URLs, credentials locations, API quirks
|
|
12
|
+
- Workarounds for tooling issues
|
|
13
|
+
|
|
14
|
+
## What NOT to Auto-Remember
|
|
15
|
+
|
|
16
|
+
- Temporary debugging state
|
|
17
|
+
- File contents (re-read them instead)
|
|
18
|
+
- Anything already captured in a shared-context file
|
|
19
|
+
- Anything documented in .claude/rules/
|
|
20
|
+
|
|
21
|
+
## Session-Scoped vs Cross-Session
|
|
22
|
+
|
|
23
|
+
When a work session is active:
|
|
24
|
+
- Decisions and progress from this session → update the session tracker body at `work-sessions/{name}/workspace/session.md` (consumed by /complete-work)
|
|
25
|
+
- Patterns, corrections, and insights that apply beyond this session → auto-memory (persists across all sessions)
|
|
26
|
+
- Don't duplicate: if something is already in the session tracker, don't also save it to auto-memory
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Product Integrity
|
|
2
|
+
|
|
3
|
+
Guard against personal biases, habits, and compensating mechanisms being encoded into product decisions. The tool should serve its users, not just its author.
|
|
4
|
+
|
|
5
|
+
## The Test
|
|
6
|
+
|
|
7
|
+
Before promoting any convention, feature, or rule: **would this help someone who doesn't share your specific problem?** If the answer is unclear, keep it local-only or user-scoped until external feedback confirms the value.
|
|
8
|
+
|
|
9
|
+
## Detection
|
|
10
|
+
|
|
11
|
+
- When a proposed feature directly mirrors a personal struggle, flag it: "This solves your specific problem. Is it general enough to ship?"
|
|
12
|
+
- When a convention assumes a particular working style, name it: "This assumes {style}. Does it help or constrain someone with a different approach?"
|
|
13
|
+
- When "I need this" is the primary justification, push for a second reason that isn't personal
|
|
14
|
+
|
|
15
|
+
## Response
|
|
16
|
+
|
|
17
|
+
- State the concern once
|
|
18
|
+
- Suggest keeping it local-only or user-scoped as a default
|
|
19
|
+
- If the user confirms it's general-purpose, proceed
|
|
20
|
+
- Don't block work — flag, then follow the decision
|
|
21
|
+
|
|
22
|
+
## Why
|
|
23
|
+
|
|
24
|
+
Tools built by their own users are especially vulnerable to this. A feature that compensates for one person's weakness becomes a rigid process that frustrates everyone else. The antidote is separating "this helps me" from "this helps users" — and defaulting to personal until proven general.
|