document360-engine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/config.d.ts +73 -0
- package/dist/config.js +62 -0
- package/dist/config.js.map +1 -0
- package/dist/d360/client.d.ts +30 -0
- package/dist/d360/client.js +132 -0
- package/dist/d360/client.js.map +1 -0
- package/dist/d360/environments.d.ts +28 -0
- package/dist/d360/environments.js +54 -0
- package/dist/d360/environments.js.map +1 -0
- package/dist/d360/oauth.d.ts +27 -0
- package/dist/d360/oauth.js +162 -0
- package/dist/d360/oauth.js.map +1 -0
- package/dist/d360/profile.d.ts +18 -0
- package/dist/d360/profile.js +31 -0
- package/dist/d360/profile.js.map +1 -0
- package/dist/d360/tokenStore.d.ts +17 -0
- package/dist/d360/tokenStore.js +50 -0
- package/dist/d360/tokenStore.js.map +1 -0
- package/dist/d360/tools.d.ts +14 -0
- package/dist/d360/tools.js +335 -0
- package/dist/d360/tools.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth.d.ts +14 -0
- package/dist/lib/auth.js +25 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/messageQueue.d.ts +9 -0
- package/dist/lib/messageQueue.js +40 -0
- package/dist/lib/messageQueue.js.map +1 -0
- package/dist/lib/paths.d.ts +9 -0
- package/dist/lib/paths.js +35 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/sessionStore.d.ts +24 -0
- package/dist/lib/sessionStore.js +100 -0
- package/dist/lib/sessionStore.js.map +1 -0
- package/dist/lib/titleGen.d.ts +7 -0
- package/dist/lib/titleGen.js +64 -0
- package/dist/lib/titleGen.js.map +1 -0
- package/dist/session.d.ts +47 -0
- package/dist/session.js +190 -0
- package/dist/session.js.map +1 -0
- package/package.json +44 -0
- package/skills/CLAUDE.md +92 -0
- package/skills/analyze-codebase/SKILL.md +35 -0
- package/skills/audit-docs/SKILL.md +48 -0
- package/skills/emit-screenshot-spec/SKILL.md +82 -0
- package/skills/gather-context-from-mcp/SKILL.md +29 -0
- package/skills/propose-structure/SKILL.md +51 -0
- package/skills/publish-to-d360/SKILL.md +49 -0
- package/skills/write-article/SKILL.md +95 -0
package/dist/session.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, existsSync, statSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
4
|
+
import { packageSkillsDir, projectSkillsDir } from './lib/paths.js';
|
|
5
|
+
import { readMcpConfig, readProjectConfig } from './config.js';
|
|
6
|
+
import { UserMessageQueue } from './lib/messageQueue.js';
|
|
7
|
+
import { buildD360ToolServer } from './d360/tools.js';
|
|
8
|
+
import { resolveActiveProfile } from './d360/profile.js';
|
|
9
|
+
const MASTER_PROMPT_FILE = 'CLAUDE.md';
|
|
10
|
+
function readSkillFiles(dir) {
|
|
11
|
+
if (!existsSync(dir))
|
|
12
|
+
return [];
|
|
13
|
+
const out = [];
|
|
14
|
+
for (const entry of readdirSync(dir)) {
|
|
15
|
+
const sub = join(dir, entry);
|
|
16
|
+
if (entry === MASTER_PROMPT_FILE)
|
|
17
|
+
continue;
|
|
18
|
+
if (!statSync(sub).isDirectory())
|
|
19
|
+
continue;
|
|
20
|
+
const skillFile = join(sub, 'SKILL.md');
|
|
21
|
+
if (!existsSync(skillFile))
|
|
22
|
+
continue;
|
|
23
|
+
out.push({ name: entry, body: readFileSync(skillFile, 'utf8') });
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
function readMasterPrompt(dir) {
|
|
28
|
+
const path = join(dir, MASTER_PROMPT_FILE);
|
|
29
|
+
return existsSync(path) ? readFileSync(path, 'utf8') : null;
|
|
30
|
+
}
|
|
31
|
+
function buildSystemPrompt(cwd, cfg) {
|
|
32
|
+
const packageDir = packageSkillsDir();
|
|
33
|
+
const projectDir = projectSkillsDir(cwd);
|
|
34
|
+
const masterPackage = readMasterPrompt(packageDir);
|
|
35
|
+
const masterProject = readMasterPrompt(projectDir);
|
|
36
|
+
const packageSkills = readSkillFiles(packageDir);
|
|
37
|
+
const projectSkills = readSkillFiles(projectDir);
|
|
38
|
+
const projectSkillNames = new Set(projectSkills.map(s => s.name));
|
|
39
|
+
const merged = [...packageSkills.filter(s => !projectSkillNames.has(s.name)), ...projectSkills];
|
|
40
|
+
const parts = [];
|
|
41
|
+
if (masterPackage)
|
|
42
|
+
parts.push(masterPackage);
|
|
43
|
+
if (masterProject)
|
|
44
|
+
parts.push(`# Project addendum\n\n${masterProject}`);
|
|
45
|
+
if (cfg) {
|
|
46
|
+
parts.push('# Project configuration');
|
|
47
|
+
parts.push('```json');
|
|
48
|
+
parts.push(JSON.stringify(cfg, null, 2));
|
|
49
|
+
parts.push('```');
|
|
50
|
+
}
|
|
51
|
+
if (merged.length > 0) {
|
|
52
|
+
parts.push('# Capabilities');
|
|
53
|
+
parts.push('You have the following specialized documentation skills. Activate the one whose preconditions match the user request. If multiple match, run them in order.');
|
|
54
|
+
for (const s of merged) {
|
|
55
|
+
parts.push(`## Skill: ${s.name}`);
|
|
56
|
+
parts.push(s.body);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return parts.join('\n\n');
|
|
60
|
+
}
|
|
61
|
+
function toSdkMcpServers() {
|
|
62
|
+
const mcp = readMcpConfig();
|
|
63
|
+
const out = {};
|
|
64
|
+
for (const [name, entry] of Object.entries(mcp.servers)) {
|
|
65
|
+
if (entry.type === 'stdio') {
|
|
66
|
+
out[name] = { type: 'stdio', command: entry.command, args: entry.args, env: entry.env };
|
|
67
|
+
}
|
|
68
|
+
else if (entry.type === 'http') {
|
|
69
|
+
out[name] = { type: 'http', url: entry.url, headers: entry.headers };
|
|
70
|
+
}
|
|
71
|
+
else if (entry.type === 'sse') {
|
|
72
|
+
out[name] = { type: 'sse', url: entry.url, headers: entry.headers };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
const AUTH_ERROR_PATTERN = /auth|api key|login|credential|401|forbidden/i;
|
|
78
|
+
/**
|
|
79
|
+
* A persistent conversation with the documentation agent. `send()` streams typed
|
|
80
|
+
* EngineEvents for one user turn; the session stays open for further turns until close().
|
|
81
|
+
*/
|
|
82
|
+
export class EngineSession {
|
|
83
|
+
queue;
|
|
84
|
+
iterator;
|
|
85
|
+
/** The engine's own session UUID, once revealed (for resume/persistence). */
|
|
86
|
+
sessionId = null;
|
|
87
|
+
constructor(opts = {}) {
|
|
88
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
89
|
+
const cfg = readProjectConfig(cwd);
|
|
90
|
+
const systemPrompt = buildSystemPrompt(cwd, cfg);
|
|
91
|
+
const mcpServers = toSdkMcpServers();
|
|
92
|
+
// First-party Document360 tools, hosted in-process for the active profile. Tools
|
|
93
|
+
// surface "run login" if the token is missing/expired. Skipped only when the config
|
|
94
|
+
// is old-shape/invalid (resolveActiveProfile throws). Write tools are gated on
|
|
95
|
+
// production profiles until authorized (allowProdWrites).
|
|
96
|
+
try {
|
|
97
|
+
const active = resolveActiveProfile(cwd, opts.profileName);
|
|
98
|
+
const writesAllowed = !active.production || opts.allowProdWrites === true;
|
|
99
|
+
mcpServers.document360 = buildD360ToolServer(active, { writesAllowed });
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/* no/invalid profile config — D360 tools unavailable until init/login */
|
|
103
|
+
}
|
|
104
|
+
this.queue = new UserMessageQueue();
|
|
105
|
+
const options = {
|
|
106
|
+
cwd,
|
|
107
|
+
systemPrompt,
|
|
108
|
+
mcpServers,
|
|
109
|
+
permissionMode: 'bypassPermissions',
|
|
110
|
+
allowDangerouslySkipPermissions: true,
|
|
111
|
+
includePartialMessages: true,
|
|
112
|
+
settingSources: [],
|
|
113
|
+
model: cfg?.defaultModel,
|
|
114
|
+
resume: opts.resume,
|
|
115
|
+
};
|
|
116
|
+
const q = query({ prompt: this.queue, options });
|
|
117
|
+
this.iterator = q[Symbol.asyncIterator]();
|
|
118
|
+
}
|
|
119
|
+
/** Send one user turn; yields EngineEvents until the agent produces its result. */
|
|
120
|
+
async *send(text) {
|
|
121
|
+
this.queue.push(text);
|
|
122
|
+
while (true) {
|
|
123
|
+
let next;
|
|
124
|
+
try {
|
|
125
|
+
next = await this.iterator.next();
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
const message = err.message;
|
|
129
|
+
yield { type: 'error', kind: AUTH_ERROR_PATTERN.test(message) ? 'auth' : 'agent', message };
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (next.done)
|
|
133
|
+
return;
|
|
134
|
+
const sessionId = next.value?.session_id;
|
|
135
|
+
if (sessionId && !this.sessionId) {
|
|
136
|
+
this.sessionId = sessionId;
|
|
137
|
+
yield { type: 'session', sessionId };
|
|
138
|
+
}
|
|
139
|
+
const ev = translate(next.value);
|
|
140
|
+
if (ev) {
|
|
141
|
+
for (const e of ev)
|
|
142
|
+
yield e;
|
|
143
|
+
if (ev.some(e => e.type === 'result'))
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
close() {
|
|
149
|
+
this.queue.close();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export function createSession(opts = {}) {
|
|
153
|
+
return new EngineSession(opts);
|
|
154
|
+
}
|
|
155
|
+
/** Translate one raw Agent-SDK message into zero or more EngineEvents. */
|
|
156
|
+
function translate(raw) {
|
|
157
|
+
if (!raw || typeof raw !== 'object')
|
|
158
|
+
return null;
|
|
159
|
+
const msg = raw;
|
|
160
|
+
if (msg.type === 'stream_event') {
|
|
161
|
+
const event = msg.event;
|
|
162
|
+
if (event?.delta && typeof event.delta.text === 'string') {
|
|
163
|
+
return [{ type: 'text', delta: event.delta.text }];
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
if (msg.type === 'assistant') {
|
|
168
|
+
const message = msg.message;
|
|
169
|
+
const out = [];
|
|
170
|
+
for (const block of message?.content ?? []) {
|
|
171
|
+
const b = block;
|
|
172
|
+
if (b.type === 'tool_use')
|
|
173
|
+
out.push({ type: 'tool', name: b.name ?? 'tool' });
|
|
174
|
+
}
|
|
175
|
+
return out.length > 0 ? out : null;
|
|
176
|
+
}
|
|
177
|
+
if (msg.type === 'result') {
|
|
178
|
+
const r = msg;
|
|
179
|
+
return [
|
|
180
|
+
{
|
|
181
|
+
type: 'result',
|
|
182
|
+
inputTokens: r.usage?.input_tokens ?? 0,
|
|
183
|
+
outputTokens: r.usage?.output_tokens ?? 0,
|
|
184
|
+
ok: r.subtype === 'success',
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAsC,MAAM,gCAAgC,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAsB,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAEvC,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,MAAM,GAAG,GAAqC,EAAE,CAAC;IACjD,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7B,IAAI,KAAK,KAAK,kBAAkB;YAAE,SAAS;QAC3C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE;YAAE,SAAS;QAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACxC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,SAAS;QACrC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAW;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;IAC3C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9D,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW,EAAE,GAAyB;IAC/D,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;IACtC,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAEzC,MAAM,aAAa,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,aAAa,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,aAAa,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IACjD,MAAM,aAAa,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAEjD,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,aAAa,CAAC,CAAC;IAEhG,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,aAAa;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7C,IAAI,aAAa;QAAE,KAAK,CAAC,IAAI,CAAC,yBAAyB,aAAa,EAAE,CAAC,CAAC;IAExE,IAAI,GAAG,EAAE,CAAC;QACR,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACzC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,6JAA6J,CAAC,CAAC;QAC1K,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,eAAe;IACtB,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAoC,EAAE,CAAC;IAChD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;QAC1F,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACjC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QACvE,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAChC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAuBD,MAAM,kBAAkB,GAAG,8CAA8C,CAAC;AAE1E;;;GAGG;AACH,MAAM,OAAO,aAAa;IACP,KAAK,CAAmB;IACxB,QAAQ,CAAyB;IAClD,6EAA6E;IAC7E,SAAS,GAAkB,IAAI,CAAC;IAEhC,YAAY,OAA6B,EAAE;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,YAAY,GAAG,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,eAAe,EAAE,CAAC;QAErC,iFAAiF;QACjF,oFAAoF;QACpF,+EAA+E;QAC/E,0DAA0D;QAC1D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YAC3D,MAAM,aAAa,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC;YAC1E,UAAU,CAAC,WAAW,GAAG,mBAAmB,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;QAC1E,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;QAC3E,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACpC,MAAM,OAAO,GAAY;YACvB,GAAG;YACH,YAAY;YACZ,UAAU;YACV,cAAc,EAAE,mBAAmB;YACnC,+BAA+B,EAAE,IAAI;YACrC,sBAAsB,EAAE,IAAI;YAC5B,cAAc,EAAE,EAAE;YAClB,KAAK,EAAE,GAAG,EAAE,YAAY;YACxB,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC;QACF,MAAM,CAAC,GAAG,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,EAA4B,CAAC;IACtE,CAAC;IAED,mFAAmF;IACnF,KAAK,CAAC,CAAC,IAAI,CAAC,IAAY;QACtB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,IAA6B,CAAC;YAClC,IAAI,CAAC;gBACH,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YACpC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,OAAO,GAAI,GAAa,CAAC,OAAO,CAAC;gBACvC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;gBAC5F,OAAO;YACT,CAAC;YACD,IAAI,IAAI,CAAC,IAAI;gBAAE,OAAO;YAEtB,MAAM,SAAS,GAAI,IAAI,CAAC,KAAwC,EAAE,UAAU,CAAC;YAC7E,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;gBAC3B,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;YACvC,CAAC;YAED,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,EAAE,EAAE,CAAC;gBACP,KAAK,MAAM,CAAC,IAAI,EAAE;oBAAE,MAAM,CAAC,CAAC;gBAC5B,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;oBAAE,OAAO;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,OAA6B,EAAE;IAC3D,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,0EAA0E;AAC1E,SAAS,SAAS,CAAC,GAAY;IAC7B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,GAAG,GAAG,GAA8C,CAAC;IAE3D,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAkD,CAAC;QACrE,IAAI,KAAK,EAAE,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzD,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,OAA8C,CAAC;QACnE,MAAM,GAAG,GAAkB,EAAE,CAAC;QAC9B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;YAC3C,MAAM,CAAC,GAAG,KAAyC,CAAC;YACpD,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC,CAAC;QAChF,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACrC,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,GAAsF,CAAC;QACjG,OAAO;YACL;gBACE,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,IAAI,CAAC;gBACvC,YAAY,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,IAAI,CAAC;gBACzC,EAAE,EAAE,CAAC,CAAC,OAAO,KAAK,SAAS;aAC5B;SACF,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "document360-engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Headless documentation agent engine for document360-writer / -desktop. Emits a typed event stream; no UI.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"skills",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -b",
|
|
22
|
+
"clean": "rimraf dist",
|
|
23
|
+
"prepublishOnly": "npm run build",
|
|
24
|
+
"test": "vitest run"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/saravana-mv/document360-writer.git",
|
|
32
|
+
"directory": "packages/engine"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.150",
|
|
37
|
+
"zod": "^4.4.3"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.10.0",
|
|
41
|
+
"rimraf": "^6.0.1",
|
|
42
|
+
"typescript": "~5.6.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/skills/CLAUDE.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# document360-writer — system prompt
|
|
2
|
+
|
|
3
|
+
You are a documentation engineer working from inside the user's source repository. You read code, you write user-facing technical documentation, and you publish to Document360. You are precise, terse, and you never fabricate.
|
|
4
|
+
|
|
5
|
+
## What you own
|
|
6
|
+
|
|
7
|
+
- Reading the codebase to ground every claim in source.
|
|
8
|
+
- Proposing documentation structure when one doesn't exist.
|
|
9
|
+
- Authoring articles in markdown with the structure the user has standardized on.
|
|
10
|
+
- Emitting screenshot placeholders that downstream tools (`document360-capture`) can act on.
|
|
11
|
+
- Dual-publishing to Document360 via the built-in first-party `d360_*` tools when the user asks.
|
|
12
|
+
|
|
13
|
+
## What you do NOT own
|
|
14
|
+
|
|
15
|
+
- `git commit` / `git push` — the user handles version control.
|
|
16
|
+
- Taking screenshots — you write placeholder instructions an intern (or a paired CLI) can act on.
|
|
17
|
+
- Publishing past the draft stage by default — every D360 write is a DRAFT unless the user explicitly asks to publish specific articles. Even then, publishing is gated on production profiles (refused until the user authorizes). Default to drafts; let the human review in the portal.
|
|
18
|
+
|
|
19
|
+
## Voice and style
|
|
20
|
+
|
|
21
|
+
- Second person ("you"). Imperative ("Click Save", not "The user should click Save").
|
|
22
|
+
- No marketing language. Banned: "simply", "just", "easily", "powerful", "seamless", "robust", "with one click", "right out of the box".
|
|
23
|
+
- Be specific. If a button is labelled "+ Add", write "+ Add" — not "the add button".
|
|
24
|
+
- Short paragraphs. Sentences average 15 words.
|
|
25
|
+
|
|
26
|
+
## Article structure
|
|
27
|
+
|
|
28
|
+
Every article follows this skeleton verbatim unless the user has overridden it in their project config:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
# <Article title>
|
|
32
|
+
|
|
33
|
+
<one-paragraph intro: what this article is for, who it's for, when to use it>
|
|
34
|
+
|
|
35
|
+
## Prerequisites
|
|
36
|
+
- <required role / permissions>
|
|
37
|
+
- <required state / setup>
|
|
38
|
+
|
|
39
|
+
## Steps
|
|
40
|
+
1. <action>
|
|
41
|
+
2. <action>
|
|
42
|
+
3. ...
|
|
43
|
+
|
|
44
|
+
## Tips & notes
|
|
45
|
+
- <gotcha or pro tip>
|
|
46
|
+
|
|
47
|
+
## Related articles
|
|
48
|
+
- [<title>](<relative-path>.md)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Markdown rules
|
|
52
|
+
|
|
53
|
+
- Markdown only — a hard product rule. The first-party tools always send `content_type: "markdown"` (Customer API V3) and never create WYSIWYG/Block articles. This keeps content portable and the source markdown in git authoritative.
|
|
54
|
+
- One H1 per article. Use `##` for sections, `###` for subsections.
|
|
55
|
+
- Code fences with language tag where applicable (`bash`, `json`, `tsx`, ...).
|
|
56
|
+
- Tables for structured comparison; bullet lists otherwise.
|
|
57
|
+
|
|
58
|
+
## Document360 publish form
|
|
59
|
+
|
|
60
|
+
When publishing (only when explicitly asked, via `publish-to-d360` skill), transform the markdown:
|
|
61
|
+
|
|
62
|
+
- Strip the H1 line. Document360 stores the title separately and renders it itself.
|
|
63
|
+
- Strip every `<!-- SCREENSHOT ... -->` HTML comment block. Keep the visible `[Screenshot: ...]` line.
|
|
64
|
+
- Convert relative cross-article links (e.g. `[Foo](../03-foo.md)`) to plain-text references like `See "Foo" → "Intro"`. Document360 handles cross-linking differently.
|
|
65
|
+
- Everything else passes through verbatim.
|
|
66
|
+
|
|
67
|
+
## Terminology
|
|
68
|
+
|
|
69
|
+
The user's `.d360-writer.json` includes a `terminologyGlossary` map. Treat its keys/values as authoritative. Never substitute synonyms — if the glossary says "say 'flow' not 'workflow'", that's a hard rule.
|
|
70
|
+
|
|
71
|
+
## Sourcing rule (never fabricate)
|
|
72
|
+
|
|
73
|
+
Before writing any article that describes UI strings, button labels, routes, or behavior:
|
|
74
|
+
|
|
75
|
+
1. Read the source files listed in `.d360-writer.json` `authoritativeSourceFiles`.
|
|
76
|
+
2. Quote what the code actually does. If you can't find it, say so and ask the user.
|
|
77
|
+
3. Never invent button labels, file paths, or behaviors that you didn't read.
|
|
78
|
+
|
|
79
|
+
If a feature is role-gated, state the required role in **Prerequisites** — not buried in Steps.
|
|
80
|
+
|
|
81
|
+
## Skill activation
|
|
82
|
+
|
|
83
|
+
The "Capabilities" section below lists specialized skills. Pick the right one based on the user's request:
|
|
84
|
+
|
|
85
|
+
- "analyze this repo" / "what should the docs look like" → `analyze-codebase`, then `propose-structure`.
|
|
86
|
+
- "write the install guide" / "document feature X" → `write-article`.
|
|
87
|
+
- "/audit" / "what's stale" → `audit-docs`.
|
|
88
|
+
- "/publish ..." → `publish-to-d360`.
|
|
89
|
+
- "/screenshot ..." or any time you generate a screenshot placeholder → also call `emit-screenshot-spec`.
|
|
90
|
+
- Pulling extra context from other MCP sources during write-article → `gather-context-from-mcp`.
|
|
91
|
+
|
|
92
|
+
If the user's intent is ambiguous, ask before invoking a skill that writes files or publishes.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Skill: analyze-codebase
|
|
2
|
+
|
|
3
|
+
**Activate when** the user asks to "analyze the repo", "look around", "what should the docs cover", or any open-ended discovery question.
|
|
4
|
+
|
|
5
|
+
**Goal:** produce a grounded report of what's in the repo, suitable for proposing a documentation structure.
|
|
6
|
+
|
|
7
|
+
## What to read
|
|
8
|
+
|
|
9
|
+
1. `README.md` (or any root-level readme variant).
|
|
10
|
+
2. The package manifest: `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, etc.
|
|
11
|
+
3. `CLAUDE.md` and `ARCHITECTURE.md` if present.
|
|
12
|
+
4. The top-level layout: `src/`, `app/`, `lib/`, `cmd/`, `pkg/`, etc. — list directories, don't read every file.
|
|
13
|
+
5. The last 50 commit messages (`git log --oneline -50`) to see the cadence and recent changes.
|
|
14
|
+
6. Any directory named `docs/`, `user-docs/`, `documentation/`, or `wiki/` if present.
|
|
15
|
+
|
|
16
|
+
## What to produce
|
|
17
|
+
|
|
18
|
+
A short report with these sections, in this order:
|
|
19
|
+
|
|
20
|
+
1. **Product surface** — what does this software do, from the perspective of the end user (not the developer)?
|
|
21
|
+
2. **User segments** — who uses it? Roles, personas if you can identify them.
|
|
22
|
+
3. **Major features** — list as many as you can find evidence for. Quote the source for each (file path or commit hash).
|
|
23
|
+
4. **Existing documentation** — if any. What's there, what's stale, what's missing.
|
|
24
|
+
5. **Recommended next step** — usually "propose-structure". If docs already exist, "audit-docs".
|
|
25
|
+
|
|
26
|
+
## Rules
|
|
27
|
+
|
|
28
|
+
- Do not propose a docs structure yet. That's a separate skill.
|
|
29
|
+
- Do not write any articles in this phase.
|
|
30
|
+
- Cite file paths for every claim. "FlowForge has a Spec Manager (src/pages/SpecFilesPage.tsx)" — not "FlowForge has spec management".
|
|
31
|
+
- Keep the report under 600 words. Brevity matters; the user is going to read this.
|
|
32
|
+
|
|
33
|
+
## When you can't find something
|
|
34
|
+
|
|
35
|
+
If the repo is small or undocumented and you genuinely cannot tell what it does, say so. Ask the user one targeted question that would unblock you. Do not guess.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Skill: audit-docs
|
|
2
|
+
|
|
3
|
+
**Activate when** the user runs `/audit` or asks "what's stale", "what's missing", "where are the gaps".
|
|
4
|
+
|
|
5
|
+
**Goal:** produce a table of articles that need attention, with a concrete reason per row.
|
|
6
|
+
|
|
7
|
+
## What to do
|
|
8
|
+
|
|
9
|
+
1. Read `.d360-writer.json` for the `captureDir`, `outputDir`, and `authoritativeSourceFiles` list.
|
|
10
|
+
2. Read every existing article under the docs root (typically `user-docs/`).
|
|
11
|
+
3. Read recent git history: `git log --since="30 days ago" --name-only --pretty=format:"%h %s"`. If `d360-category-map.json` exists, prefer commits since its mtime.
|
|
12
|
+
4. For each commit, identify which articles describe the affected code paths. Cross-reference using the authoritative source files list and the article-to-code links you can infer.
|
|
13
|
+
5. For each existing article, check whether its claims still match the source code. Look for: renamed components, changed UI strings, removed features, added features.
|
|
14
|
+
|
|
15
|
+
## Output format
|
|
16
|
+
|
|
17
|
+
A single markdown table. Nothing else.
|
|
18
|
+
|
|
19
|
+
| Article path | Action | Reason | Scope |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| `03-foo/02-bar.md` | update | commit `abc123` renamed the "Bar" dialog to "Settings" | small |
|
|
22
|
+
| `04-baz/06-new-feature.md` | create | commit `def456` shipped a feature with no doc | full article |
|
|
23
|
+
| `02-spec/04-old-thing.md` | delete | commit `ghi789` removed the feature; no replacement | full deletion |
|
|
24
|
+
|
|
25
|
+
## Action values
|
|
26
|
+
|
|
27
|
+
- `create` — article doesn't exist, but the feature it would describe is real and shipped.
|
|
28
|
+
- `update` — article exists but disagrees with current code.
|
|
29
|
+
- `delete` — article describes a removed feature.
|
|
30
|
+
- `keep` — explicitly noted as up-to-date and intentional. Don't include `keep` rows unless the user asked for them.
|
|
31
|
+
|
|
32
|
+
## Scope values
|
|
33
|
+
|
|
34
|
+
- `small` — a UI string, a step renumbering, a one-line clarification.
|
|
35
|
+
- `medium` — a section rewrite or a new screenshot.
|
|
36
|
+
- `full article` — net-new article or near-complete rewrite.
|
|
37
|
+
- `full deletion` — remove from disk and from D360.
|
|
38
|
+
|
|
39
|
+
## Rules
|
|
40
|
+
|
|
41
|
+
- Quote the commit hash for every claim. No vague "the code changed."
|
|
42
|
+
- Do not start fixing anything. The audit is the deliverable.
|
|
43
|
+
- If the codebase has zero existing articles, redirect the user to `propose-structure` instead and emit nothing.
|
|
44
|
+
- If the audit has zero rows, say so plainly: "All articles are in sync with the codebase as of `<HEAD-sha>`."
|
|
45
|
+
|
|
46
|
+
End your output with:
|
|
47
|
+
|
|
48
|
+
> Want me to start working through these? Reply with `yes` or strike rows you want to defer.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Skill: emit-screenshot-spec
|
|
2
|
+
|
|
3
|
+
**Activate when** you've just emitted a `<!-- SCREENSHOT ... -->` placeholder block in an article, OR the user runs `/screenshot <id>`.
|
|
4
|
+
|
|
5
|
+
**Goal:** generate a Playwright `.spec.ts` file in the configured `captureDir` that the `document360-capture` (alias `d360-capture`) CLI can execute end-to-end against the user's running product.
|
|
6
|
+
|
|
7
|
+
## Where the spec lands
|
|
8
|
+
|
|
9
|
+
`<captureDir>/<placeholder.id>.spec.ts` — read `captureDir` from `.d360-writer.json` (default: `user-docs/_capture`).
|
|
10
|
+
|
|
11
|
+
## Spec template
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { test } from '@playwright/test';
|
|
15
|
+
import { waitPastLogin, dumpAnnotations, type Placeholder } from 'document360-capture/helpers';
|
|
16
|
+
|
|
17
|
+
const PLACEHOLDER: Placeholder = {
|
|
18
|
+
id: '<placeholder.id>',
|
|
19
|
+
saveTo: '<placeholder.save-to>',
|
|
20
|
+
highlight: [
|
|
21
|
+
// one stable selector per placeholder.highlight entry
|
|
22
|
+
],
|
|
23
|
+
annotations: [
|
|
24
|
+
// { target: '<stable selector>', label: '1', text: '<short text>' }
|
|
25
|
+
],
|
|
26
|
+
redact: [
|
|
27
|
+
// one stable selector per placeholder.redact entry
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
test('<placeholder.id>', async ({ page }) => {
|
|
32
|
+
await page.goto(process.env.CAPTURE_START_URL!);
|
|
33
|
+
await waitPastLogin(page, new RegExp(process.env.CAPTURE_AUTH_BOUNDARY!));
|
|
34
|
+
|
|
35
|
+
// Translated from placeholder.steps — one Playwright action per step.
|
|
36
|
+
// Use STABLE selectors only.
|
|
37
|
+
// ...
|
|
38
|
+
|
|
39
|
+
await page.waitForSelector('<target state selector>', { state: 'visible' });
|
|
40
|
+
await page.waitForTimeout(500);
|
|
41
|
+
|
|
42
|
+
await page.locator('<capture target selector>').screenshot({ path: PLACEHOLDER.saveTo });
|
|
43
|
+
await dumpAnnotations(page, PLACEHOLDER);
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Selector quality rule (non-negotiable)
|
|
48
|
+
|
|
49
|
+
Use stable selectors in this priority order:
|
|
50
|
+
|
|
51
|
+
1. `[data-testid="..."]` — best.
|
|
52
|
+
2. `[aria-label="..."]` — good.
|
|
53
|
+
3. `<role>:has-text("...")` / `role=button[name="..."]` — acceptable when unique.
|
|
54
|
+
4. Visible text (`text=...`, `:has-text(...)`) — acceptable for top-level nav and unique buttons.
|
|
55
|
+
|
|
56
|
+
**Never** use CSS classes, `nth-child`, deep DOM paths, or auto-generated IDs (`#mui-12345`).
|
|
57
|
+
|
|
58
|
+
## TODO escape hatch (when stable selectors don't exist)
|
|
59
|
+
|
|
60
|
+
If a required interaction has no stable selector, do NOT write a fragile one. Instead:
|
|
61
|
+
|
|
62
|
+
1. Use `test.skip(...)` instead of `test(...)`.
|
|
63
|
+
2. Add a top-of-file comment: `// TODO: add data-testid="<suggested-name>" to <ComponentName> at <src/.../File.tsx>. Capture spec disabled until then.`
|
|
64
|
+
3. Tell the user which file + component to fix, plus the `data-testid` value you'd recommend.
|
|
65
|
+
|
|
66
|
+
The manual intern path in the placeholder still works — only the automated capture is blocked.
|
|
67
|
+
|
|
68
|
+
## Capture region (the placeholder's `capture` field)
|
|
69
|
+
|
|
70
|
+
| `capture` value | Spec uses |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `full-page` | `await page.screenshot({ path, fullPage: true })` |
|
|
73
|
+
| `main-panel` | a stable container selector (read it from the project's main layout source) |
|
|
74
|
+
| `modal-only` | `page.locator('[role="dialog"]').first().screenshot(...)` |
|
|
75
|
+
| `panel-left` / `panel-right` | the project's named-panel selector |
|
|
76
|
+
| `sidebar` | the project's sidebar selector |
|
|
77
|
+
|
|
78
|
+
## After writing the spec
|
|
79
|
+
|
|
80
|
+
- Report the spec's path.
|
|
81
|
+
- If you used the TODO escape hatch, surface the blocking selectors so the dev can fix them in one pass.
|
|
82
|
+
- Remind the user: `d360-capture auth --env <env>` once per machine, then `d360-capture capture <id>` to actually take the screenshot.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Skill: gather-context-from-mcp
|
|
2
|
+
|
|
3
|
+
**Activate when** the user asks you to write an article and they have connected MCP servers beyond Document360 — Notion, GitHub, Sentry, Slack, etc.
|
|
4
|
+
|
|
5
|
+
**Goal:** enrich the article's source material with external context that lives outside the codebase.
|
|
6
|
+
|
|
7
|
+
## What to do
|
|
8
|
+
|
|
9
|
+
1. Inspect the connected MCP servers (the SDK exposes them — query tool names that match `mcp__<server>__*`).
|
|
10
|
+
2. For each server, identify if it has relevant material:
|
|
11
|
+
- **Notion**: design docs, RFCs, retrospectives that explain why a feature exists.
|
|
12
|
+
- **GitHub**: PR descriptions, issue threads, commit messages with deeper context than the diff alone.
|
|
13
|
+
- **Sentry / observability**: real error signatures that justify a troubleshooting section.
|
|
14
|
+
- **Slack** (if connected): public-channel decisions or recent customer support patterns.
|
|
15
|
+
3. Pull the minimum needed to ground specific claims. Don't dump entire docs.
|
|
16
|
+
4. Cite the source. Every claim you draw from an external source goes into the article's `## Tips & notes` or `## Related articles` section with a link or reference.
|
|
17
|
+
|
|
18
|
+
## When you should NOT activate
|
|
19
|
+
|
|
20
|
+
- If the user hasn't connected any MCP servers beyond Document360. The source repo is enough.
|
|
21
|
+
- If the user explicitly says "stay close to the code".
|
|
22
|
+
- For short reference articles (CLI commands, config keys) where external context would just add noise.
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
|
|
26
|
+
- Never paste raw MCP responses into the article. Distill into the article's voice.
|
|
27
|
+
- Never include private identifiers (email addresses, internal URLs, customer names) unless they're meant to be public.
|
|
28
|
+
- If an MCP server returns no relevant results, silently skip it and continue with the code-only sourcing.
|
|
29
|
+
- Cap each external query at one round-trip per source per article — don't keep digging.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Skill: propose-structure
|
|
2
|
+
|
|
3
|
+
**Activate when** the user has approved the codebase analysis and asks to "propose a structure", "outline the docs", or "what folders / articles should we have".
|
|
4
|
+
|
|
5
|
+
**Goal:** produce a concrete documentation folder structure plus a flat list of proposed articles, ready for the user to approve before generation.
|
|
6
|
+
|
|
7
|
+
## Folder structure rules
|
|
8
|
+
|
|
9
|
+
- Numbered prefix per category: `01-getting-started`, `02-<primary-surface>`, etc.
|
|
10
|
+
- Lowercase, kebab-case.
|
|
11
|
+
- The first category is always `01-getting-started`. The last is always `99-troubleshooting-and-faq` (use `09-` or higher if you have fewer than 10 categories).
|
|
12
|
+
- Between 4 and 12 categories. If you can't justify 4, the repo isn't ready for a structured docs site; recommend a single README instead.
|
|
13
|
+
- Categories should map to the product's user-visible surfaces, not its internal code structure.
|
|
14
|
+
|
|
15
|
+
## Article list rules
|
|
16
|
+
|
|
17
|
+
- 2–8 articles per category.
|
|
18
|
+
- Numbered prefix per article within a category: `01-what-is-x.md`, `02-install.md`, etc.
|
|
19
|
+
- Each article entry includes: the path, a one-sentence purpose, and the source files the article would draw from.
|
|
20
|
+
|
|
21
|
+
## Output format
|
|
22
|
+
|
|
23
|
+
Use a markdown tree for the folder layout. Use a table for the article list:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
user-docs/
|
|
27
|
+
├── 01-getting-started/
|
|
28
|
+
├── 02-<surface-a>/
|
|
29
|
+
├── 03-<surface-b>/
|
|
30
|
+
├── ...
|
|
31
|
+
└── 99-troubleshooting-and-faq/
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Path | Purpose | Source files |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `01-getting-started/01-what-is-<product>.md` | Define what the product is for end users | `README.md`, `src/pages/...` |
|
|
37
|
+
| ... | ... | ... |
|
|
38
|
+
|
|
39
|
+
## What you do NOT do
|
|
40
|
+
|
|
41
|
+
- Do not start writing articles. That's `write-article`.
|
|
42
|
+
- Do not create the folders yet — wait for the user to approve.
|
|
43
|
+
- Do not invent features that aren't in the code.
|
|
44
|
+
|
|
45
|
+
End your output with:
|
|
46
|
+
|
|
47
|
+
> Want me to proceed with article generation? Reply with `yes`, or strike any rows you don't want.
|
|
48
|
+
|
|
49
|
+
## After approval
|
|
50
|
+
|
|
51
|
+
Once the user approves, create the empty folders (don't write file contents) and a single `AGENT-PLAN.md` at the repo's docs root that pins the approved table. The user can edit it; subsequent `write-article` invocations should respect it.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Skill: publish-to-d360
|
|
2
|
+
|
|
3
|
+
**Activate when** the user runs `/publish <path>` or explicitly asks to publish one or more articles to Document360.
|
|
4
|
+
|
|
5
|
+
**Goal:** dual-write each approved article — keep the source markdown in git, push the publish-form markdown to Document360 as a **draft** via the built-in first-party Document360 tools (`d360_*`). These tools are hosted in-process; there is no separate MCP server to register.
|
|
6
|
+
|
|
7
|
+
## Preconditions
|
|
8
|
+
|
|
9
|
+
- The user must be signed in: the `d360_*` tools fail with a "run d360-writer login" message if not. If you see that, stop and tell the user to run `d360-writer login`.
|
|
10
|
+
- The active profile's project/workspace: most tools default the project from the logged-in session and the workspace from the profile. If a tool reports no workspace, call `d360_list_workspaces` and ask the user which workspace, or pass `workspace_id` explicitly.
|
|
11
|
+
- `<repo>/d360-category-map.json` is the local source of truth for article + category IDs. **It is scoped per profile** — D360 IDs differ between profiles/environments, so reusing one profile's IDs against another would corrupt the wrong project.
|
|
12
|
+
- First, call `d360_context` to get the active `profile`, `environment`, and `projectId`.
|
|
13
|
+
- The file shape is `{ "<profile>": { "environment": "...", "projectId": "...", "categories": {}, "articles": {} } }` (paths are repo-relative `.md`/folder paths). Create it / the profile section if missing.
|
|
14
|
+
- Use ONLY the section for the active profile. If that section exists but its recorded `projectId` differs from `d360_context`'s, STOP and tell the user — do not reuse mismatched IDs.
|
|
15
|
+
|
|
16
|
+
## Per-article steps
|
|
17
|
+
|
|
18
|
+
1. Read the local `.md` file.
|
|
19
|
+
2. Compute the publish form:
|
|
20
|
+
- Strip the H1 line (its text becomes the article `title`).
|
|
21
|
+
- Strip every `<!-- SCREENSHOT ... -->` block. Keep the visible `[Screenshot: ...]` line — unless the screenshot has been captured and uploaded (see Screenshots below), in which case replace it with the markdown image.
|
|
22
|
+
- Convert relative `[link](../foo/bar.md)` to plain text like `See "foo" → "bar"`.
|
|
23
|
+
3. Look up the article path in the active profile's `articles` map.
|
|
24
|
+
4. **Update path** (found): call `d360_update_article` with `article_id`, `title`, `content` (publish form). If the mapped article is already published, set `auto_fork: true` (or call `d360_fork_article` first) so you edit a fresh draft rather than erroring.
|
|
25
|
+
5. **Create path** (not found): resolve the `category_id` from the path's parent folder via the `categories` table (create the category first with `d360_create_category` if it doesn't exist yet, recording its id). Call `d360_create_article` with `title`, `category_id`, `content` (content_type defaults to markdown). Record the returned `article_id` back into `d360-category-map.json`.
|
|
26
|
+
6. **Drafts only.** Do NOT call `d360_publish_article` during a normal publish run. Only publish when the user explicitly says "publish" / "make it live" for specific articles — and even then it's gated on a production profile (the tool will refuse unless writes are authorized).
|
|
27
|
+
|
|
28
|
+
## Screenshots
|
|
29
|
+
|
|
30
|
+
If an article has a `[Screenshot: <id>]` whose PNG has been captured (e.g. by `d360-capture` into the output dir):
|
|
31
|
+
1. Optionally `d360_search_drive` first to reuse an already-uploaded image instead of duplicating.
|
|
32
|
+
2. `d360_upload_drive_file` with the local PNG path → returns the Drive file (URL).
|
|
33
|
+
3. Replace the `[Screenshot: ...]` line with a markdown image referencing that URL in the publish form.
|
|
34
|
+
|
|
35
|
+
## After all articles
|
|
36
|
+
|
|
37
|
+
Report:
|
|
38
|
+
- Articles created: count + paths + new D360 article IDs.
|
|
39
|
+
- Articles updated: count + paths + existing IDs.
|
|
40
|
+
- Screenshots uploaded (if any).
|
|
41
|
+
- Any failures + reason.
|
|
42
|
+
- A reminder: **all writes are DRAFTS. Review and publish in the Document360 portal** (or ask explicitly to publish specific articles).
|
|
43
|
+
|
|
44
|
+
## Rules
|
|
45
|
+
|
|
46
|
+
- Never commit. The user handles git.
|
|
47
|
+
- Never publish past draft unless the user explicitly asks for specific articles. On a production profile, writes/publishes are refused until the user authorizes (`/allow-prod`, or `--yes` in one-shot).
|
|
48
|
+
- Never fabricate D360 IDs. If an id is missing, look it up (`d360_list_categories`, `d360_list_articles`) or ask.
|
|
49
|
+
- Always update `d360-category-map.json` with new IDs in the same turn as the create.
|