whygraph 0.1.0 → 0.1.1

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.
@@ -126,7 +126,8 @@ export function registerInitCommand(program) {
126
126
  ` App node: ${result.appId}\n` +
127
127
  ` Config: ${result.configPath}\n` +
128
128
  mcpLine +
129
- `\nRun 'whygraph up' to start the server.\n`);
129
+ `\nRun 'npx whygraph up' to start the server.\n` +
130
+ `Run 'npx whygraph onboard' to get a prompt for building your graph.\n`);
130
131
  }
131
132
  }
132
133
  catch (err) {
@@ -0,0 +1,9 @@
1
+ import type { Command } from "commander";
2
+ export interface OnboardResult {
3
+ appId: string;
4
+ appName: string;
5
+ prefix: string;
6
+ prompt: string;
7
+ }
8
+ export declare function runOnboard(targetDir: string): OnboardResult;
9
+ export declare function registerOnboardCommand(program: Command): void;
@@ -0,0 +1,109 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import yaml from "js-yaml";
4
+ import { parseEntity } from "../../entity/parser.js";
5
+ import { findWhygraphDir } from "./serve.js";
6
+ // ============================================================
7
+ // Core Logic
8
+ // ============================================================
9
+ export function runOnboard(targetDir) {
10
+ const projectDir = findWhygraphDir(targetDir);
11
+ if (!projectDir) {
12
+ throw new Error(`.whygraph/ not found. Run "npx whygraph init" first.`);
13
+ }
14
+ const configPath = join(projectDir, ".whygraph", "config.yaml");
15
+ if (!existsSync(configPath)) {
16
+ throw new Error(`.whygraph/config.yaml not found. Run "npx whygraph init" first.`);
17
+ }
18
+ const raw = readFileSync(configPath, "utf-8");
19
+ const config = yaml.load(raw);
20
+ // Find the App node
21
+ const graphDir = join(projectDir, ".whygraph", "graph");
22
+ let appId = "";
23
+ let appName = config.appName;
24
+ if (existsSync(graphDir)) {
25
+ const files = readdirSync(graphDir).filter((f) => f.endsWith(".md"));
26
+ for (const file of files) {
27
+ const content = readFileSync(join(graphDir, file), "utf-8");
28
+ const entity = parseEntity(content);
29
+ if (entity !== null && entity.label === "App") {
30
+ appId = entity.id;
31
+ /* v8 ignore next 1 */
32
+ if ("name" in entity)
33
+ appName = entity.name;
34
+ break;
35
+ }
36
+ }
37
+ }
38
+ if (!appId) {
39
+ throw new Error(`No App node found in .whygraph/graph/. Run "npx whygraph init" first.`);
40
+ }
41
+ const prompt = buildOnboardingPrompt(appName, appId, config.prefix);
42
+ return { appId, appName, prefix: config.prefix, prompt };
43
+ }
44
+ function buildOnboardingPrompt(appName, appId, prefix) {
45
+ return [
46
+ `I'm using whygraph to maintain a living decision graph for ${appName}.`,
47
+ `The root App node is \`${appId}\`.`,
48
+ ``,
49
+ `Please explore this codebase and build out the structural graph by:`,
50
+ ``,
51
+ `1. Identifying the top-level features or modules (e.g. authentication, data pipeline, API layer, UI pages).`,
52
+ ` For each one, create a Feature node using the whygraph MCP tool \`whygraph_create_node\`:`,
53
+ ` - label: Feature`,
54
+ ` - parent: ${appId}`,
55
+ ` - name: <feature name>`,
56
+ ` - status: active`,
57
+ ``,
58
+ `2. For each Feature, identify its key components (e.g. services, hooks, controllers, presenters).`,
59
+ ` For each one, create a Component node:`,
60
+ ` - label: Component`,
61
+ ` - parent: <feature id>`,
62
+ ` - name: <component name>`,
63
+ ` - status: active`,
64
+ ``,
65
+ `3. Use the \`refs\` field to link nodes to their source files where relevant.`,
66
+ ``,
67
+ `4. After building the structural graph, review the existing decisions in .whygraph/graph/ and`,
68
+ ` update their \`affects\` fields to link them to the relevant Feature or Component nodes you created.`,
69
+ ``,
70
+ `Node IDs should use the prefix \`${prefix}\` followed by a 4-character nanoid (e.g. \`${prefix}abcd\`).`,
71
+ ``,
72
+ `Start by listing the top-level directories and key entry points, then propose the Feature list`,
73
+ `before creating any nodes. I'll confirm before you proceed.`,
74
+ ].join("\n");
75
+ }
76
+ // ============================================================
77
+ // CLI Wiring
78
+ // ============================================================
79
+ export function registerOnboardCommand(program) {
80
+ program
81
+ .command("onboard")
82
+ .description("Print a codebase analysis prompt to bootstrap your whygraph graph")
83
+ .option("--json", "Output results as JSON")
84
+ .action((opts) => {
85
+ try {
86
+ const result = runOnboard(process.cwd());
87
+ if (opts.json) {
88
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
89
+ }
90
+ else {
91
+ process.stdout.write(`Paste the following prompt into your AI agent:\n\n` +
92
+ `${"─".repeat(60)}\n` +
93
+ `${result.prompt}\n` +
94
+ `${"─".repeat(60)}\n`);
95
+ }
96
+ }
97
+ catch (err) {
98
+ /* v8 ignore next 1 */
99
+ const message = err instanceof Error ? err.message : String(err);
100
+ if (opts.json) {
101
+ process.stdout.write(JSON.stringify({ error: message }, null, 2) + "\n");
102
+ }
103
+ else {
104
+ process.stderr.write(`Error: ${message}\n`);
105
+ }
106
+ process.exitCode = 1;
107
+ }
108
+ });
109
+ }
package/dist/cli/index.js CHANGED
@@ -8,6 +8,7 @@ import { registerDownCommand } from "./commands/down.js";
8
8
  import { registerInitCommand } from "./commands/init.js";
9
9
  import { registerIssuesCommand } from "./commands/issues.js";
10
10
  import { registerMcpCommand } from "./commands/mcp.js";
11
+ import { registerOnboardCommand } from "./commands/onboard.js";
11
12
  import { registerRestartCommand } from "./commands/restart.js";
12
13
  import { registerServeCommand } from "./commands/serve.js";
13
14
  import { registerStatusCommand } from "./commands/status.js";
@@ -24,6 +25,7 @@ registerDownCommand(program);
24
25
  registerInitCommand(program);
25
26
  registerIssuesCommand(program);
26
27
  registerMcpCommand(program);
28
+ registerOnboardCommand(program);
27
29
  registerRestartCommand(program);
28
30
  registerServeCommand(program);
29
31
  registerStatusCommand(program);
@@ -66,8 +66,8 @@ function generateInstructions(config) {
66
66
  "",
67
67
  "### Server Status",
68
68
  "",
69
- "Run `whygraph status` to check if the server is running.",
70
- "Run `whygraph up` to start the server.",
69
+ "Run `npx whygraph status` to check if the server is running.",
70
+ "Run `npx whygraph up` to start the server.",
71
71
  "",
72
72
  ].join("\n");
73
73
  }
@@ -197,7 +197,7 @@ export function writePlatformRules(projectDir, environment, _primeOutput, config
197
197
  case "copilot": {
198
198
  const mcpRegistered = registerMcpWithCopilot(projectDir);
199
199
  const mcpSetupPath = mcpRegistered ? undefined : writeMcpSetupMd(projectDir, environment);
200
- return { ...writeAgentsMd(projectDir, instructions, environment), mcpRegistered, mcpSetupPath };
200
+ return { ...writeCopilotInstructions(projectDir, instructions), mcpRegistered, mcpSetupPath };
201
201
  }
202
202
  case "other": {
203
203
  const mcpSetupPath = writeMcpSetupMd(projectDir, environment);
@@ -227,3 +227,17 @@ function writeAgentsMd(projectDir, instructions, environment) {
227
227
  writeFileSync(filePath, newContent, "utf-8");
228
228
  return { environment, filePath };
229
229
  }
230
+ function writeCopilotInstructions(projectDir, instructions) {
231
+ const githubDir = join(projectDir, ".github");
232
+ if (!existsSync(githubDir)) {
233
+ mkdirSync(githubDir, { recursive: true });
234
+ }
235
+ const filePath = join(githubDir, "copilot-instructions.md");
236
+ let existing = "";
237
+ if (existsSync(filePath)) {
238
+ existing = readFileSync(filePath, "utf-8");
239
+ }
240
+ const newContent = upsertMarkedSection(existing, instructions);
241
+ writeFileSync(filePath, newContent, "utf-8");
242
+ return { environment: "copilot", filePath };
243
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whygraph",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "The graph of why. So your agent knows before it touches anything.",
5
5
  "author": "Geovanie Ruiz",
6
6
  "license": "MIT",