document360-writer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/dist/agent.d.ts +6 -0
  4. package/dist/agent.js +92 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +26 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/audit.d.ts +2 -0
  10. package/dist/commands/audit.js +12 -0
  11. package/dist/commands/audit.js.map +1 -0
  12. package/dist/commands/clear.d.ts +2 -0
  13. package/dist/commands/clear.js +6 -0
  14. package/dist/commands/clear.js.map +1 -0
  15. package/dist/commands/exit.d.ts +2 -0
  16. package/dist/commands/exit.js +4 -0
  17. package/dist/commands/exit.js.map +1 -0
  18. package/dist/commands/help.d.ts +2 -0
  19. package/dist/commands/help.js +27 -0
  20. package/dist/commands/help.js.map +1 -0
  21. package/dist/commands/index.d.ts +18 -0
  22. package/dist/commands/index.js +38 -0
  23. package/dist/commands/index.js.map +1 -0
  24. package/dist/commands/init.d.ts +2 -0
  25. package/dist/commands/init.js +85 -0
  26. package/dist/commands/init.js.map +1 -0
  27. package/dist/commands/load.d.ts +2 -0
  28. package/dist/commands/load.js +20 -0
  29. package/dist/commands/load.js.map +1 -0
  30. package/dist/commands/mcp.d.ts +2 -0
  31. package/dist/commands/mcp.js +83 -0
  32. package/dist/commands/mcp.js.map +1 -0
  33. package/dist/commands/publish.d.ts +2 -0
  34. package/dist/commands/publish.js +19 -0
  35. package/dist/commands/publish.js.map +1 -0
  36. package/dist/commands/save.d.ts +2 -0
  37. package/dist/commands/save.js +29 -0
  38. package/dist/commands/save.js.map +1 -0
  39. package/dist/commands/screenshot.d.ts +2 -0
  40. package/dist/commands/screenshot.js +20 -0
  41. package/dist/commands/screenshot.js.map +1 -0
  42. package/dist/config.d.ts +40 -0
  43. package/dist/config.js +42 -0
  44. package/dist/config.js.map +1 -0
  45. package/dist/lib/colors.d.ts +8 -0
  46. package/dist/lib/colors.js +10 -0
  47. package/dist/lib/colors.js.map +1 -0
  48. package/dist/lib/messageQueue.d.ts +9 -0
  49. package/dist/lib/messageQueue.js +40 -0
  50. package/dist/lib/messageQueue.js.map +1 -0
  51. package/dist/lib/paths.d.ts +10 -0
  52. package/dist/lib/paths.js +38 -0
  53. package/dist/lib/paths.js.map +1 -0
  54. package/dist/repl.d.ts +5 -0
  55. package/dist/repl.js +132 -0
  56. package/dist/repl.js.map +1 -0
  57. package/package.json +54 -0
  58. package/skills/CLAUDE.md +92 -0
  59. package/skills/analyze-codebase/SKILL.md +35 -0
  60. package/skills/audit-docs/SKILL.md +48 -0
  61. package/skills/emit-screenshot-spec/SKILL.md +82 -0
  62. package/skills/gather-context-from-mcp/SKILL.md +29 -0
  63. package/skills/propose-structure/SKILL.md +51 -0
  64. package/skills/publish-to-d360/SKILL.md +39 -0
  65. package/skills/write-article/SKILL.md +93 -0
@@ -0,0 +1,2 @@
1
+ import type { SlashCommandResult } from './index.js';
2
+ export declare function saveCommand(args: string[]): Promise<SlashCommandResult>;
@@ -0,0 +1,29 @@
1
+ import { error, ok, warn } from '../lib/colors.js';
2
+ import { sessionPath, sessionsDir, ensureDir } from '../lib/paths.js';
3
+ import { existsSync, copyFileSync } from 'node:fs';
4
+ export async function saveCommand(args) {
5
+ const name = args[0];
6
+ if (!name) {
7
+ console.log(error('Usage: /save <name>'));
8
+ return { kind: 'continue' };
9
+ }
10
+ ensureDir(sessionsDir());
11
+ const dest = sessionPath(name);
12
+ const transcript = locateActiveTranscript();
13
+ if (!transcript) {
14
+ console.log(warn('No active session transcript was found to copy.'));
15
+ console.log(warn('The Agent SDK is configured with settingSources: [] for SDK isolation, so transcripts are not persisted automatically in this build.'));
16
+ console.log(warn(`Manual save target: ${dest}`));
17
+ return { kind: 'continue' };
18
+ }
19
+ copyFileSync(transcript, dest);
20
+ console.log(ok(`✓ Saved session to ${dest}`));
21
+ return { kind: 'continue' };
22
+ }
23
+ function locateActiveTranscript() {
24
+ const candidate = process.env.D360_WRITER_TRANSCRIPT;
25
+ if (candidate && existsSync(candidate))
26
+ return candidate;
27
+ return null;
28
+ }
29
+ //# sourceMappingURL=save.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"save.js","sourceRoot":"","sources":["../../src/commands/save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAc;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAC1C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;IAED,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;IACzB,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAE/B,MAAM,UAAU,GAAG,sBAAsB,EAAE,CAAC;IAC5C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,sIAAsI,CAAC,CAAC,CAAC;QAC1J,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC,CAAC;QACjD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;IAED,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAC9B,CAAC;AAED,SAAS,sBAAsB;IAC7B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;IACrD,IAAI,SAAS,IAAI,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACzD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { SlashCommandResult } from './index.js';
2
+ export declare function screenshotCommand(args: string[]): Promise<SlashCommandResult>;
@@ -0,0 +1,20 @@
1
+ import { error } from '../lib/colors.js';
2
+ export async function screenshotCommand(args) {
3
+ const id = args[0];
4
+ if (!id) {
5
+ console.log(error('Usage: /screenshot <placeholder-id>'));
6
+ return { kind: 'continue' };
7
+ }
8
+ const prompt = [
9
+ `Run the emit-screenshot-spec skill for placeholder id: ${id}`,
10
+ '',
11
+ 'Steps you must follow:',
12
+ '1. Locate the SCREENSHOT HTML comment block with this id across user-docs/**/*.md.',
13
+ '2. Translate the placeholder.steps into Playwright actions using STABLE selectors only (data-testid > aria-label > role+name > visible text). If no stable selector is available for a required action, write `test.skip(...)` plus a TODO comment naming the React component that needs a data-testid.',
14
+ '3. Write the spec file to <captureDir>/<id>.spec.ts. Use `import { test } from "@playwright/test"` and `import { waitPastLogin, dumpAnnotations, type Placeholder } from "document360-capture/helpers"`.',
15
+ "4. Use process.env.CAPTURE_START_URL and process.env.CAPTURE_AUTH_BOUNDARY (these are injected at run time by document360-capture; do not hardcode them).",
16
+ "5. Report the path of the generated spec and any TODOs from missing selectors.",
17
+ ].join('\n');
18
+ return { kind: 'forward-to-agent', prompt };
19
+ }
20
+ //# sourceMappingURL=screenshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAGzC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAc;IACpD,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC,CAAC;QAC1D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;IACD,MAAM,MAAM,GAAG;QACb,0DAA0D,EAAE,EAAE;QAC9D,EAAE;QACF,wBAAwB;QACxB,oFAAoF;QACpF,ySAAyS;QACzS,0MAA0M;QAC1M,2JAA2J;QAC3J,gFAAgF;KACjF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,40 @@
1
+ export type ProjectConfig = {
2
+ projectId: string;
3
+ d360?: {
4
+ projectId?: string;
5
+ projectVersionId?: string;
6
+ helpCenterBaseUrl?: string;
7
+ };
8
+ captureDir?: string;
9
+ outputDir?: string;
10
+ authoritativeSourceFiles?: string[];
11
+ terminologyGlossary?: Record<string, string>;
12
+ defaultModel?: string;
13
+ };
14
+ export type UserConfig = {
15
+ defaultModel?: string;
16
+ autoUpdate?: boolean;
17
+ };
18
+ export type McpServerEntry = {
19
+ type: 'stdio';
20
+ command: string;
21
+ args?: string[];
22
+ env?: Record<string, string>;
23
+ } | {
24
+ type: 'http';
25
+ url: string;
26
+ headers?: Record<string, string>;
27
+ } | {
28
+ type: 'sse';
29
+ url: string;
30
+ headers?: Record<string, string>;
31
+ };
32
+ export type UserMcpConfig = {
33
+ servers: Record<string, McpServerEntry>;
34
+ };
35
+ export declare function readProjectConfig(cwd?: string): ProjectConfig | null;
36
+ export declare function writeProjectConfig(cfg: ProjectConfig, cwd?: string): void;
37
+ export declare function readUserConfig(): UserConfig;
38
+ export declare function writeUserConfig(cfg: UserConfig): void;
39
+ export declare function readMcpConfig(): UserMcpConfig;
40
+ export declare function writeMcpConfig(cfg: UserMcpConfig): void;
package/dist/config.js ADDED
@@ -0,0 +1,42 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
+ import { projectConfigPath, userConfigPath, userMcpConfigPath, userDir, ensureDir, } from './lib/paths.js';
3
+ export function readProjectConfig(cwd = process.cwd()) {
4
+ const path = projectConfigPath(cwd);
5
+ if (!existsSync(path))
6
+ return null;
7
+ return JSON.parse(readFileSync(path, 'utf8'));
8
+ }
9
+ export function writeProjectConfig(cfg, cwd = process.cwd()) {
10
+ writeFileSync(projectConfigPath(cwd), JSON.stringify(cfg, null, 2) + '\n', 'utf8');
11
+ }
12
+ export function readUserConfig() {
13
+ const path = userConfigPath();
14
+ if (!existsSync(path))
15
+ return {};
16
+ try {
17
+ return JSON.parse(readFileSync(path, 'utf8'));
18
+ }
19
+ catch {
20
+ return {};
21
+ }
22
+ }
23
+ export function writeUserConfig(cfg) {
24
+ ensureDir(userDir());
25
+ writeFileSync(userConfigPath(), JSON.stringify(cfg, null, 2) + '\n', 'utf8');
26
+ }
27
+ export function readMcpConfig() {
28
+ const path = userMcpConfigPath();
29
+ if (!existsSync(path))
30
+ return { servers: {} };
31
+ try {
32
+ return JSON.parse(readFileSync(path, 'utf8'));
33
+ }
34
+ catch {
35
+ return { servers: {} };
36
+ }
37
+ }
38
+ export function writeMcpConfig(cfg) {
39
+ ensureDir(userDir());
40
+ writeFileSync(userMcpConfigPath(), JSON.stringify(cfg, null, 2) + '\n', 'utf8');
41
+ }
42
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,OAAO,EACP,SAAS,GACV,MAAM,gBAAgB,CAAC;AA8BxB,MAAM,UAAU,iBAAiB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC3D,MAAM,IAAI,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAkB,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAkB,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IAChF,aAAa,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AACrF,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAe,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAe;IAC7C,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;IACrB,aAAa,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAC;IACjC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC9C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAkB,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAkB;IAC/C,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;IACrB,aAAa,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAClF,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare const banner: (s: string) => string;
2
+ export declare const dim: (s: string) => string;
3
+ export declare const error: (s: string) => string;
4
+ export declare const warn: (s: string) => string;
5
+ export declare const ok: (s: string) => string;
6
+ export declare const accent: (s: string) => string;
7
+ export declare const muted: (s: string) => string;
8
+ export declare const bold: (s: string) => string;
@@ -0,0 +1,10 @@
1
+ import pc from 'picocolors';
2
+ export const banner = (s) => pc.bold(pc.cyan(s));
3
+ export const dim = (s) => pc.dim(s);
4
+ export const error = (s) => pc.red(s);
5
+ export const warn = (s) => pc.yellow(s);
6
+ export const ok = (s) => pc.green(s);
7
+ export const accent = (s) => pc.cyan(s);
8
+ export const muted = (s) => pc.gray(s);
9
+ export const bold = (s) => pc.bold(s);
10
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/lib/colors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACjE,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACpD,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACtD,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACrD,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { SDKUserMessage } from '@anthropic-ai/claude-agent-sdk';
2
+ export declare class UserMessageQueue implements AsyncIterable<SDKUserMessage> {
3
+ private resolvers;
4
+ private buffer;
5
+ private closed;
6
+ push(content: string): void;
7
+ close(): void;
8
+ [Symbol.asyncIterator](): AsyncIterator<SDKUserMessage>;
9
+ }
@@ -0,0 +1,40 @@
1
+ export class UserMessageQueue {
2
+ resolvers = [];
3
+ buffer = [];
4
+ closed = false;
5
+ push(content) {
6
+ if (this.closed)
7
+ return;
8
+ const msg = {
9
+ type: 'user',
10
+ message: { role: 'user', content },
11
+ parent_tool_use_id: null,
12
+ };
13
+ const resolver = this.resolvers.shift();
14
+ if (resolver)
15
+ resolver(msg);
16
+ else
17
+ this.buffer.push(msg);
18
+ }
19
+ close() {
20
+ this.closed = true;
21
+ for (const r of this.resolvers)
22
+ r(null);
23
+ this.resolvers = [];
24
+ }
25
+ async *[Symbol.asyncIterator]() {
26
+ while (true) {
27
+ if (this.buffer.length > 0) {
28
+ yield this.buffer.shift();
29
+ continue;
30
+ }
31
+ if (this.closed)
32
+ return;
33
+ const next = await new Promise(r => this.resolvers.push(r));
34
+ if (next === null)
35
+ return;
36
+ yield next;
37
+ }
38
+ }
39
+ }
40
+ //# sourceMappingURL=messageQueue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messageQueue.js","sourceRoot":"","sources":["../../src/lib/messageQueue.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,gBAAgB;IACnB,SAAS,GAAgD,EAAE,CAAC;IAC5D,MAAM,GAAqB,EAAE,CAAC;IAC9B,MAAM,GAAG,KAAK,CAAC;IAEvB,IAAI,CAAC,OAAe;QAClB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,MAAM,GAAG,GAAmB;YAC1B,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;YAClC,kBAAkB,EAAE,IAAI;SACzB,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACxC,IAAI,QAAQ;YAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;;YACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS;YAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;QAC3B,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAG,CAAC;gBAC3B,SAAS;YACX,CAAC;YACD,IAAI,IAAI,CAAC,MAAM;gBAAE,OAAO;YACxB,MAAM,IAAI,GAAG,MAAM,IAAI,OAAO,CAAwB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACnF,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO;YAC1B,MAAM,IAAI,CAAC;QACb,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ export declare function packageRoot(): string;
2
+ export declare function packageSkillsDir(): string;
3
+ export declare function userDir(): string;
4
+ export declare function userConfigPath(): string;
5
+ export declare function userMcpConfigPath(): string;
6
+ export declare function sessionsDir(): string;
7
+ export declare function sessionPath(name: string): string;
8
+ export declare function projectConfigPath(cwd?: string): string;
9
+ export declare function projectSkillsDir(cwd?: string): string;
10
+ export declare function ensureDir(path: string): void;
@@ -0,0 +1,38 @@
1
+ import { homedir } from 'node:os';
2
+ import { join, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { existsSync, mkdirSync } from 'node:fs';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+ export function packageRoot() {
8
+ return join(__dirname, '..', '..');
9
+ }
10
+ export function packageSkillsDir() {
11
+ return join(packageRoot(), 'skills');
12
+ }
13
+ export function userDir() {
14
+ return join(homedir(), '.document360-writer');
15
+ }
16
+ export function userConfigPath() {
17
+ return join(userDir(), 'config.json');
18
+ }
19
+ export function userMcpConfigPath() {
20
+ return join(userDir(), 'mcp.json');
21
+ }
22
+ export function sessionsDir() {
23
+ return join(userDir(), 'sessions');
24
+ }
25
+ export function sessionPath(name) {
26
+ return join(sessionsDir(), `${name}.jsonl`);
27
+ }
28
+ export function projectConfigPath(cwd = process.cwd()) {
29
+ return join(cwd, '.d360-writer.json');
30
+ }
31
+ export function projectSkillsDir(cwd = process.cwd()) {
32
+ return join(cwd, '.d360-writer', 'skills');
33
+ }
34
+ export function ensureDir(path) {
35
+ if (!existsSync(path))
36
+ mkdirSync(path, { recursive: true });
37
+ }
38
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,qBAAqB,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,GAAG,IAAI,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC3D,OAAO,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC1D,OAAO,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC"}
package/dist/repl.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export type ReplContext = {
2
+ cwd: string;
3
+ restartAgent: () => void;
4
+ };
5
+ export declare function runRepl(cwd?: string): Promise<void>;
package/dist/repl.js ADDED
@@ -0,0 +1,132 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { startAgent } from './agent.js';
3
+ import { slashCommands, parseSlash } from './commands/index.js';
4
+ import { accent, banner, dim, error, muted } from './lib/colors.js';
5
+ export async function runRepl(cwd = process.cwd()) {
6
+ if (!process.env.ANTHROPIC_API_KEY) {
7
+ console.error('');
8
+ console.error(error('ANTHROPIC_API_KEY is not set.'));
9
+ console.error('');
10
+ console.error('Set it before running document360-writer:');
11
+ console.error('');
12
+ console.error(` ${accent('export ANTHROPIC_API_KEY=sk-ant-...')} (macOS / Linux)`);
13
+ console.error(` ${accent('$env:ANTHROPIC_API_KEY="sk-ant-..."')} (PowerShell)`);
14
+ console.error('');
15
+ console.error(`Get a key at ${accent('https://console.anthropic.com/settings/keys')}`);
16
+ console.error('');
17
+ process.exit(2);
18
+ }
19
+ printWelcome(cwd);
20
+ let session = startAgent(cwd);
21
+ const ctx = {
22
+ cwd,
23
+ restartAgent: () => {
24
+ session.queue.close();
25
+ session = startAgent(cwd);
26
+ },
27
+ };
28
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
29
+ try {
30
+ while (true) {
31
+ const line = await rl.question(accent('> '));
32
+ const trimmed = line.trim();
33
+ if (!trimmed)
34
+ continue;
35
+ if (trimmed.startsWith('/')) {
36
+ const parsed = parseSlash(trimmed);
37
+ if (!parsed)
38
+ continue;
39
+ const cmd = slashCommands[parsed.name];
40
+ if (!cmd) {
41
+ console.log(error(`Unknown command: /${parsed.name}`));
42
+ console.log(dim('Type /help for the list.'));
43
+ continue;
44
+ }
45
+ const result = await cmd(parsed.args, ctx);
46
+ if (result.kind === 'exit')
47
+ break;
48
+ if (result.kind === 'clear') {
49
+ ctx.restartAgent();
50
+ continue;
51
+ }
52
+ if (result.kind === 'forward-to-agent') {
53
+ session.queue.push(result.prompt);
54
+ await consumeUntilResult(session.iterator);
55
+ }
56
+ continue;
57
+ }
58
+ session.queue.push(trimmed);
59
+ await consumeUntilResult(session.iterator);
60
+ }
61
+ }
62
+ finally {
63
+ session.queue.close();
64
+ rl.close();
65
+ }
66
+ }
67
+ function printWelcome(cwd) {
68
+ console.log('');
69
+ console.log(banner('document360-writer'));
70
+ console.log(dim(` cwd: ${cwd}`));
71
+ console.log(dim(' Type a prompt, or /help for slash commands. /exit to quit.'));
72
+ console.log('');
73
+ }
74
+ async function consumeUntilResult(iterator) {
75
+ while (true) {
76
+ let next;
77
+ try {
78
+ next = await iterator.next();
79
+ }
80
+ catch (err) {
81
+ console.error('');
82
+ console.error(error(`agent error: ${err.message}`));
83
+ return;
84
+ }
85
+ if (next.done)
86
+ return;
87
+ const stop = renderMessage(next.value);
88
+ if (stop)
89
+ return;
90
+ }
91
+ }
92
+ function renderMessage(raw) {
93
+ if (!raw || typeof raw !== 'object')
94
+ return false;
95
+ const msg = raw;
96
+ if (msg.type === 'partial_assistant') {
97
+ const event = msg.event;
98
+ if (event?.delta && typeof event.delta.text === 'string') {
99
+ process.stdout.write(event.delta.text);
100
+ }
101
+ return false;
102
+ }
103
+ if (msg.type === 'assistant') {
104
+ const message = msg.message;
105
+ const blocks = message?.content ?? [];
106
+ let printedNewline = false;
107
+ for (const block of blocks) {
108
+ const b = block;
109
+ if (b.type === 'tool_use') {
110
+ if (!printedNewline) {
111
+ process.stdout.write('\n');
112
+ printedNewline = true;
113
+ }
114
+ console.log(muted(` ⚙ ${b.name ?? 'tool'}`));
115
+ }
116
+ }
117
+ return false;
118
+ }
119
+ if (msg.type === 'result') {
120
+ const result = msg;
121
+ process.stdout.write('\n');
122
+ if (result.usage) {
123
+ const inT = result.usage.input_tokens ?? 0;
124
+ const outT = result.usage.output_tokens ?? 0;
125
+ console.log(dim(` (${inT}→${outT} tokens)`));
126
+ }
127
+ console.log('');
128
+ return true;
129
+ }
130
+ return false;
131
+ }
132
+ //# sourceMappingURL=repl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repl.js","sourceRoot":"","sources":["../src/repl.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "document360-writer",
3
+ "version": "0.1.0",
4
+ "description": "Standalone documentation agent CLI. Reads your code, writes your docs. Specialized for Document360 publishing.",
5
+ "type": "module",
6
+ "bin": {
7
+ "document360-writer": "./dist/cli.js",
8
+ "d360-writer": "./dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "skills",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc -b",
18
+ "clean": "rimraf dist",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/saravana-mv/document360-writer.git",
27
+ "directory": "packages/writer"
28
+ },
29
+ "homepage": "https://github.com/saravana-mv/document360-writer#readme",
30
+ "bugs": {
31
+ "url": "https://github.com/saravana-mv/document360-writer/issues"
32
+ },
33
+ "dependencies": {
34
+ "@anthropic-ai/claude-agent-sdk": "^0.3.150",
35
+ "@inquirer/prompts": "^8.4.3",
36
+ "commander": "^14.0.3",
37
+ "picocolors": "^1.1.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.10.0",
41
+ "rimraf": "^6.0.1",
42
+ "typescript": "~5.6.0"
43
+ },
44
+ "keywords": [
45
+ "documentation",
46
+ "agent",
47
+ "cli",
48
+ "document360",
49
+ "anthropic",
50
+ "claude",
51
+ "mcp"
52
+ ],
53
+ "license": "MIT"
54
+ }
@@ -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 MCP 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 — every D360 write is a draft; the human reviews and publishes manually 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. Document360's API expects `contentType=0`.
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.