@sagebase/cli 1.0.0 → 1.0.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.
- package/dist/index.js +217 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,9 +6,8 @@ import { Command as Command3 } from "commander";
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import { constants } from "fs";
|
|
9
|
-
import { access, mkdir,
|
|
10
|
-
import {
|
|
11
|
-
import { fileURLToPath } from "url";
|
|
9
|
+
import { access, mkdir, writeFile } from "fs/promises";
|
|
10
|
+
import { resolve as resolve2 } from "path";
|
|
12
11
|
import { z as z2 } from "zod";
|
|
13
12
|
|
|
14
13
|
// src/utils/logger.ts
|
|
@@ -350,10 +349,218 @@ function parsePnpmWorkspacePackages(content) {
|
|
|
350
349
|
return patterns;
|
|
351
350
|
}
|
|
352
351
|
|
|
352
|
+
// src/utils/templates.ts
|
|
353
|
+
var INDEX_MDX = `---
|
|
354
|
+
title: Welcome
|
|
355
|
+
description: Welcome to our help center
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
# Welcome
|
|
359
|
+
|
|
360
|
+
This help center is maintained by Sage \u2014 an AI agent that keeps your documentation accurate as you ship.
|
|
361
|
+
|
|
362
|
+
## Getting started
|
|
363
|
+
|
|
364
|
+
Replace this sample content with your own documentation.
|
|
365
|
+
`;
|
|
366
|
+
var SAGE_WORKFLOW = `import { defineAgent, defineWorkflow } from "@flue/runtime";
|
|
367
|
+
import { local } from "@flue/runtime/node";
|
|
368
|
+
import * as v from "valibot";
|
|
369
|
+
|
|
370
|
+
const agent = defineAgent(() => ({
|
|
371
|
+
sandbox: local({
|
|
372
|
+
env: {
|
|
373
|
+
GH_TOKEN: process.env.GH_TOKEN,
|
|
374
|
+
},
|
|
375
|
+
}),
|
|
376
|
+
model: process.env.SAGE_MODEL ?? "anthropic/claude-sonnet-4-6",
|
|
377
|
+
instructions: \`You are Sage, an AI documentation agent. Your job is to analyze code changes and update documentation.
|
|
378
|
+
|
|
379
|
+
## Frontmatter requirements
|
|
380
|
+
Every MDX file MUST start with valid YAML frontmatter:
|
|
381
|
+
---
|
|
382
|
+
title: Page Title
|
|
383
|
+
description: Brief description
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Instructions
|
|
387
|
+
1. Analyze the code diff to understand what changed
|
|
388
|
+
2. Create new MDX files for new features/modules
|
|
389
|
+
3. Update existing MDX files when related code changes
|
|
390
|
+
4. Use clear, concise technical writing
|
|
391
|
+
5. Include code examples where appropriate
|
|
392
|
+
|
|
393
|
+
## Output format
|
|
394
|
+
Return a JSON array of file updates. ONLY include files that need creating or updating:
|
|
395
|
+
[
|
|
396
|
+
{
|
|
397
|
+
"path": "relative/path/from-content-dir/file.mdx",
|
|
398
|
+
"content": "---\\\\ntitle: ...\\\\ndescription: ...\\\\n---\\\\n\\\\nFull MDX content here"
|
|
399
|
+
}
|
|
400
|
+
]\`,
|
|
401
|
+
}));
|
|
402
|
+
|
|
403
|
+
export default defineWorkflow({
|
|
404
|
+
agent,
|
|
405
|
+
input: v.object({
|
|
406
|
+
contentDir: v.optional(v.string(), "content/docs"),
|
|
407
|
+
diffSinceSha: v.optional(v.string()),
|
|
408
|
+
}),
|
|
409
|
+
output: v.object({
|
|
410
|
+
filesUpdated: v.number(),
|
|
411
|
+
prUrl: v.optional(v.string()),
|
|
412
|
+
}),
|
|
413
|
+
async run({ harness, input, log }) {
|
|
414
|
+
const session = await harness.session();
|
|
415
|
+
const contentDir = input.contentDir;
|
|
416
|
+
|
|
417
|
+
// 1. Get git diff
|
|
418
|
+
const mergeSha =
|
|
419
|
+
input.diffSinceSha ||
|
|
420
|
+
(await session.shell('git log --merges -1 --format="%H"')).stdout.trim();
|
|
421
|
+
|
|
422
|
+
const diff = await session.shell(
|
|
423
|
+
\`git diff \${mergeSha}..HEAD --diff-filter=ACMR -- .\`,
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
if (!diff.stdout.trim()) {
|
|
427
|
+
log.info("no code changes detected \u2014 nothing to document");
|
|
428
|
+
return { filesUpdated: 0 };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const changedFiles = (
|
|
432
|
+
await session.shell(
|
|
433
|
+
\`git diff \${mergeSha}..HEAD --diff-filter=ACMR --name-only -- .\`,
|
|
434
|
+
)
|
|
435
|
+
).stdout
|
|
436
|
+
.trim()
|
|
437
|
+
.split("\\n")
|
|
438
|
+
.filter(Boolean);
|
|
439
|
+
|
|
440
|
+
// 2. Read existing docs
|
|
441
|
+
const existingDocs = (
|
|
442
|
+
await session.shell(
|
|
443
|
+
\`find \${contentDir} -name '*.mdx' -type f 2>/dev/null | sort\`,
|
|
444
|
+
)
|
|
445
|
+
).stdout
|
|
446
|
+
.trim()
|
|
447
|
+
.split("\\n")
|
|
448
|
+
.filter(Boolean);
|
|
449
|
+
|
|
450
|
+
const existingContent: { path: string; content: string }[] = [];
|
|
451
|
+
for (const doc of existingDocs) {
|
|
452
|
+
const content = (await session.shell(\`cat "\${doc}" 2>/dev/null\`)).stdout
|
|
453
|
+
.trim()
|
|
454
|
+
.slice(0, 2000);
|
|
455
|
+
existingContent.push({ path: doc, content });
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// 3. Call LLM to analyze diff and determine doc updates
|
|
459
|
+
let existingContext = "";
|
|
460
|
+
if (existingContent.length > 0) {
|
|
461
|
+
existingContext =
|
|
462
|
+
"\\n\\n## Existing documentation\\n" +
|
|
463
|
+
existingContent
|
|
464
|
+
.map((d) => \`### \${d.path}\\n\\\`\\\`\\\`mdx\\n\${d.content}\\n\\\`\\\`\\\`\`)
|
|
465
|
+
.join("\\n");
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const userMessage = \`Documentation files live in \\\`\${contentDir}\\\`. Create and update MDX files there.\\n\\nHere is the code diff:\\n\\n\\\`\\\`\\\`diff\\n\${diff.stdout.slice(0, 50000)}\\n\\\`\\\`\\\`\\n\\nChanged files:\\n\${changedFiles.map((f) => \`- \${f}\`).join("\\n")}\${existingContext}\`;
|
|
469
|
+
|
|
470
|
+
const { data } = await session.prompt(userMessage, {
|
|
471
|
+
result: v.object({
|
|
472
|
+
updates: v.array(
|
|
473
|
+
v.object({
|
|
474
|
+
path: v.string(),
|
|
475
|
+
content: v.string(),
|
|
476
|
+
}),
|
|
477
|
+
),
|
|
478
|
+
}),
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
if (data.updates.length === 0) {
|
|
482
|
+
log.info("no documentation updates needed");
|
|
483
|
+
return { filesUpdated: 0 };
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// 4. Write MDX files
|
|
487
|
+
for (const update of data.updates) {
|
|
488
|
+
const fullPath = \`\${contentDir}/\${update.path}\`;
|
|
489
|
+
await harness.fs.writeFile(fullPath, update.content);
|
|
490
|
+
log.info("wrote", { path: fullPath });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// 5. Create PR
|
|
494
|
+
const timestamp = Date.now();
|
|
495
|
+
const branchName = \`sage/docs-\${timestamp}\`;
|
|
496
|
+
|
|
497
|
+
const branchExists = (
|
|
498
|
+
await session.shell(\`git branch --list '\${branchName}'\`)
|
|
499
|
+
).stdout.trim();
|
|
500
|
+
|
|
501
|
+
if (branchExists) {
|
|
502
|
+
await session.shell(\`git checkout "\${branchName}"\`);
|
|
503
|
+
} else {
|
|
504
|
+
await session.shell(\`git checkout -b "\${branchName}"\`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
await session.shell("git add -A");
|
|
508
|
+
|
|
509
|
+
const hasChanges = (
|
|
510
|
+
await session.shell("git status --porcelain")
|
|
511
|
+
).stdout.trim();
|
|
512
|
+
if (!hasChanges) {
|
|
513
|
+
log.info("no changes to commit");
|
|
514
|
+
return { filesUpdated: data.updates.length };
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
await session.shell("git commit -m 'docs: update documentation via Sage'");
|
|
518
|
+
await session.shell(\`git push origin "\${branchName}" -u\`);
|
|
519
|
+
|
|
520
|
+
const prUrl = (
|
|
521
|
+
await session.shell(
|
|
522
|
+
\`gh pr create --title "docs: update documentation via Sage" --body "Automated documentation update by Sage.\\n\\nFiles changed:\\n\${data.updates.map((u: { path: string; content: string }) => \`- \\\`\${u.path}\\\`\`).join("\\n")}"\`,
|
|
523
|
+
)
|
|
524
|
+
).stdout.trim();
|
|
525
|
+
|
|
526
|
+
log.info("PR created", { url: prUrl });
|
|
527
|
+
|
|
528
|
+
return { filesUpdated: data.updates.length, prUrl };
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
`;
|
|
532
|
+
var FLUE_CONFIG = `import { defineConfig } from "@flue/cli/config";
|
|
533
|
+
|
|
534
|
+
export default defineConfig({
|
|
535
|
+
target: "node",
|
|
536
|
+
});
|
|
537
|
+
`;
|
|
538
|
+
var CI_WORKFLOW = `name: Sage Documentation
|
|
539
|
+
on:
|
|
540
|
+
push:
|
|
541
|
+
branches: [main]
|
|
542
|
+
jobs:
|
|
543
|
+
sage:
|
|
544
|
+
runs-on: ubuntu-latest
|
|
545
|
+
permissions:
|
|
546
|
+
contents: write
|
|
547
|
+
pull-requests: write
|
|
548
|
+
steps:
|
|
549
|
+
- uses: actions/checkout@v4
|
|
550
|
+
with:
|
|
551
|
+
fetch-depth: 0
|
|
552
|
+
- uses: actions/setup-node@v4
|
|
553
|
+
with:
|
|
554
|
+
node-version: 22
|
|
555
|
+
- run: npm ci
|
|
556
|
+
- name: Run Sage
|
|
557
|
+
env:
|
|
558
|
+
ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
|
|
559
|
+
GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
560
|
+
run: npx flue run sage
|
|
561
|
+
`;
|
|
562
|
+
|
|
353
563
|
// src/commands/init.ts
|
|
354
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
355
|
-
var __dirname = dirname(__filename);
|
|
356
|
-
var initDir = resolve2(__dirname, "../init");
|
|
357
564
|
var initOptionsSchema = z2.object({
|
|
358
565
|
cwd: z2.string().optional(),
|
|
359
566
|
defaults: z2.boolean().optional(),
|
|
@@ -385,25 +592,19 @@ var init = new Command().name("init").alias("create").description("initialize sa
|
|
|
385
592
|
config.framework = framework;
|
|
386
593
|
config.content = contentDir;
|
|
387
594
|
await mkdir(resolve2(cwd, "content", "docs"), { recursive: true });
|
|
388
|
-
const [contentSample, flueWorkflow, flueConfig, workflowYml] = await Promise.all([
|
|
389
|
-
readFile(resolve2(initDir, "content/docs/index.mdx"), "utf-8"),
|
|
390
|
-
readFile(resolve2(initDir, "flue/workflows/sage.ts"), "utf-8"),
|
|
391
|
-
readFile(resolve2(initDir, "flue/config.ts"), "utf-8"),
|
|
392
|
-
readFile(resolve2(initDir, ".github/workflows/sage.yml"), "utf-8")
|
|
393
|
-
]);
|
|
394
595
|
const indexMdxPath = resolve2(cwd, contentDir, "index.mdx");
|
|
395
596
|
try {
|
|
396
597
|
await access(indexMdxPath, constants.F_OK);
|
|
397
598
|
} catch {
|
|
398
|
-
await writeFile(indexMdxPath,
|
|
599
|
+
await writeFile(indexMdxPath, INDEX_MDX);
|
|
399
600
|
}
|
|
400
601
|
const flueWorkflowsDir = resolve2(cwd, "src", "workflows");
|
|
401
602
|
await mkdir(flueWorkflowsDir, { recursive: true });
|
|
402
|
-
await writeFile(resolve2(flueWorkflowsDir, "sage.ts"),
|
|
403
|
-
await writeFile(resolve2(cwd, "flue.config.ts"),
|
|
603
|
+
await writeFile(resolve2(flueWorkflowsDir, "sage.ts"), SAGE_WORKFLOW);
|
|
604
|
+
await writeFile(resolve2(cwd, "flue.config.ts"), FLUE_CONFIG);
|
|
404
605
|
const workflowsDir = resolve2(cwd, ".github", "workflows");
|
|
405
606
|
await mkdir(workflowsDir, { recursive: true });
|
|
406
|
-
await writeFile(resolve2(workflowsDir, "sage.yml"),
|
|
607
|
+
await writeFile(resolve2(workflowsDir, "sage.yml"), CI_WORKFLOW);
|
|
407
608
|
await writeFile(
|
|
408
609
|
resolve2(cwd, "sage.json"),
|
|
409
610
|
JSON.stringify(config, null, 2)
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/logger.ts","../src/utils/spinner.ts","../src/utils/handle-error.ts","../src/utils/update-dependencies.ts","../src/utils/get-package-manager.ts","../src/utils/get-config.ts","../src/utils/get-project-info.ts","../src/utils/get-monorepo-info.ts","../src/utils/highlighter.ts","../src/commands/dev.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport { init } from \"./commands/init.js\";\nimport { dev } from \"./commands/dev.js\";\n\nprocess.on(\"SIGINT\", () => process.exit(0));\nprocess.on(\"SIGTERM\", () => process.exit(0));\n\nasync function main() {\n const program = new Command()\n .name(\"sage\")\n .description(\"AI documentation agent for your codebase\")\n .version(\"0.0.1\");\n\n program.addCommand(init);\n program.addCommand(dev);\n\n program.parse();\n}\n\nmain();\n","import { Command } from \"commander\";\nimport { constants } from \"fs\";\nimport { access, mkdir, readFile, writeFile } from \"fs/promises\";\nimport { dirname, resolve } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { z } from \"zod\";\nimport { logger } from \"../utils/logger.js\";\nimport { createSpinner } from \"../utils/spinner.js\";\nimport { handleError } from \"../utils/handle-error.js\";\nimport { ensureFlueDeps } from \"../utils/update-dependencies.js\";\nimport { getDefaultConfig } from \"../utils/get-config.js\";\nimport {\n detectContentDir,\n detectFramework,\n} from \"../utils/get-project-info.js\";\nimport {\n formatMonorepoMessage,\n getMonorepoTargets,\n isMonorepoRoot,\n} from \"../utils/get-monorepo-info.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst initDir = resolve(__dirname, \"../init\");\n\nconst initOptionsSchema = z.object({\n cwd: z.string().optional(),\n defaults: z.boolean().optional(),\n helpCenter: z.boolean().optional(),\n});\n\n// TODO: we need a robust test fixture for both mono-repo, non-monorepo, next, tanstack; shadcn has all figured that out so we can just copy that approach for this; doing it manually is painstaking and always leads to some error cuz i have to do it and i'm lazy\n//\n// DO this before implementing second issue;\nexport const init = new Command()\n .name(\"init\")\n .alias(\"create\")\n .description(\"initialize sage in your project\")\n .argument(\"[dir]\", \"project directory\")\n .option(\"--cwd <cwd>\", \"working directory\")\n .option(\"--defaults\", \"use default configuration without prompting\")\n .option(\"--help-center\", \"add Fumadocs help center\")\n .action(async (dir, opts) => {\n try {\n const options = initOptionsSchema.parse(opts);\n const cwd = resolve(dir || options.cwd || process.cwd());\n\n // pnpm --filter=@sagebase/cli start:dev init ./apps/web\n // {} /Users/bishalneupane/Desktop/myprojects/sagebase/packages/cli/apps/web\n // Relative path does not work, have to pass absolute stuff; maybe fix this?? prioritize maybe the process.cwd more?? that would probably create yet another set of problems right????\n // This worked: bun sage init ~/Desktop/myprojects/sagebase/apps/web\n //\n // init didn't add any cli script into package.json how is the user supposed to run dev now\n // maybe again same npx sage dev command?? right\n //\n //\n // Would it be possible to avoid creating yet anaother config file cuz we're literally drowing in them\n\n const spinner = createSpinner(\"Setting up Sage...\").start();\n\n const framework = detectFramework(cwd);\n if (!framework) {\n // Check if we're in a monorepo root before failing.\n if (await isMonorepoRoot(cwd)) {\n const targets = await getMonorepoTargets(cwd);\n if (targets.length > 0) {\n spinner.stop();\n formatMonorepoMessage(\"init\", targets);\n process.exit(1);\n }\n }\n spinner.fail(\n \"Could not detect framework. Only Next.js and TanStack Start are supported.\",\n );\n process.exit(1);\n }\n\n // TODO: maybe here we could use the default fuma docs cli here instead of trying to do this stuff manaully we could have more options from there; but that means we are getting joined with fuma-docs so there is a bit of trade-off there; we won't be able to control the whole setting up experience\n // how does the better-t-setup does this things well??\n //\n // Better idea is to let the user do the setup themselves directly using the fuma-docs cli and configure everything they want, and we don't deal with all that complexity ourselves and doing that will make ur repo both easy and we'll not be tied to fuma-docs as well;\n const existingContent = detectContentDir(cwd);\n const contentDir = existingContent || \"content/docs\";\n\n const config = getDefaultConfig();\n config.framework = framework;\n config.content = contentDir;\n\n await mkdir(resolve(cwd, \"content\", \"docs\"), { recursive: true });\n\n const [contentSample, flueWorkflow, flueConfig, workflowYml] =\n await Promise.all([\n readFile(resolve(initDir, \"content/docs/index.mdx\"), \"utf-8\"),\n readFile(resolve(initDir, \"flue/workflows/sage.ts\"), \"utf-8\"),\n readFile(resolve(initDir, \"flue/config.ts\"), \"utf-8\"),\n readFile(resolve(initDir, \".github/workflows/sage.yml\"), \"utf-8\"),\n ]);\n\n const indexMdxPath = resolve(cwd, contentDir, \"index.mdx\");\n try {\n await access(indexMdxPath, constants.F_OK);\n } catch {\n await writeFile(indexMdxPath, contentSample);\n }\n\n // Even in mono repo it adds the workflow to apps/web/.github** is that the right way?? Need answer on that??\n // Scaffold Flue workflow\n // It has to be workflows cuz that's what flue cli expects\n const flueWorkflowsDir = resolve(cwd, \"src\", \"workflows\");\n await mkdir(flueWorkflowsDir, { recursive: true });\n await writeFile(resolve(flueWorkflowsDir, \"sage.ts\"), flueWorkflow);\n await writeFile(resolve(cwd, \"flue.config.ts\"), flueConfig);\n\n // Scaffold CI workflow\n const workflowsDir = resolve(cwd, \".github\", \"workflows\");\n await mkdir(workflowsDir, { recursive: true });\n await writeFile(resolve(workflowsDir, \"sage.yml\"), workflowYml);\n\n await writeFile(\n resolve(cwd, \"sage.json\"),\n JSON.stringify(config, null, 2),\n );\n\n await ensureFlueDeps(cwd);\n\n spinner.succeed(\"Sage initialized!\");\n\n logger.break();\n logger.success(\"Created:\");\n logger.log(` sage.json — configuration`);\n logger.log(` src/workflows/ — Flue agent workflow`);\n logger.log(` ${contentDir}/ — documentation source`);\n logger.log(` .github/workflows/sage.yml — CI workflow`);\n logger.break();\n logger.info(\"Next steps:\");\n logger.log(\" Run sage dev to preview your help center\");\n logger.log(\" Run sage run to generate documentation from code changes\");\n logger.log(\" Edit src/workflows/sage.ts to customize the agent\");\n logger.break();\n\n if (!existingContent) {\n logger.info(\"Created: content/docs/index.mdx — sample article\");\n }\n } catch (error) {\n handleError(error);\n }\n });\n","import kleur from \"kleur\"\n\nexport const logger = {\n error(...args: unknown[]) {\n console.log(kleur.red(\"✗\"), ...args)\n },\n warn(...args: unknown[]) {\n console.log(kleur.yellow(\"⚠\"), ...args)\n },\n info(...args: unknown[]) {\n console.log(kleur.cyan(\"ℹ\"), ...args)\n },\n success(...args: unknown[]) {\n console.log(kleur.green(\"✓\"), ...args)\n },\n log(...args: unknown[]) {\n console.log(...args)\n },\n break() {\n console.log(\"\")\n },\n}\n","import ora from \"ora\"\n\nexport function createSpinner(text: string) {\n const spinner = ora({\n text,\n color: \"cyan\",\n })\n\n return {\n start() {\n spinner.start()\n return this\n },\n succeed(text?: string) {\n spinner.succeed(text)\n return this\n },\n fail(text?: string) {\n spinner.fail(text)\n return this\n },\n stop() {\n spinner.stop()\n return this\n },\n set text(val: string) {\n spinner.text = val\n },\n }\n}\n","import { logger } from \"./logger.js\"\n\nexport function handleError(error: unknown) {\n if (error instanceof Error) {\n logger.error(error.message)\n } else {\n logger.error(String(error))\n }\n\n process.exit(1)\n}\n","import { existsSync, readFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { execa } from \"execa\";\nimport {\n getPackageManager,\n type PackageManager,\n} from \"./get-package-manager.js\";\nimport { createSpinner } from \"./spinner.js\";\n\nconst FLUE_DEPS: string[] = [];\nconst FLUE_DEV_DEPS = [\"@flue/runtime\", \"@flue/cli\", \"valibot\"];\n\nfunction getInstalledDeps(cwd: string): Set<string> {\n try {\n const pkgPath = join(cwd, \"package.json\");\n if (!existsSync(pkgPath)) return new Set();\n\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return new Set([\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ]);\n } catch {\n return new Set();\n }\n}\n\nfunction getMissingDeps(cwd: string): { deps: string[]; devDeps: string[] } {\n const installed = getInstalledDeps(cwd);\n return {\n deps: FLUE_DEPS.filter((dep) => !installed.has(dep)),\n devDeps: FLUE_DEV_DEPS.filter((dep) => !installed.has(dep)),\n };\n}\n\nasync function installWithPackageManager(\n pm: PackageManager,\n deps: string[],\n devDeps: string[],\n cwd: string,\n) {\n if (pm === \"npm\") {\n if (deps.length) await execa(\"npm\", [\"install\", ...deps], { cwd });\n if (devDeps.length)\n await execa(\"npm\", [\"install\", \"-D\", ...devDeps], { cwd });\n } else {\n if (deps.length) await execa(pm, [\"add\", ...deps], { cwd });\n if (devDeps.length) await execa(pm, [\"add\", \"-D\", ...devDeps], { cwd });\n }\n}\n\nexport async function ensureFlueDeps(cwd: string) {\n const { deps, devDeps } = getMissingDeps(cwd);\n const allMissing = [...deps, ...devDeps];\n if (allMissing.length === 0) return;\n\n const spinner = createSpinner(\n `Installing ${allMissing.join(\", \")}...`,\n ).start();\n\n try {\n const pm = await getPackageManager(cwd);\n await installWithPackageManager(pm, deps, devDeps, cwd);\n spinner.succeed();\n } catch (error) {\n spinner.fail(\"Failed to install Flue dependencies\");\n throw error;\n }\n}\n","import { existsSync } from \"fs\"\nimport { join } from \"path\"\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\"\n\nexport async function getPackageManager(\n cwd: string\n): Promise<PackageManager> {\n if (existsSync(join(cwd, \"pnpm-lock.yaml\"))) return \"pnpm\"\n if (existsSync(join(cwd, \"yarn.lock\"))) return \"yarn\"\n if (existsSync(join(cwd, \"bun.lock\"))) return \"bun\"\n if (existsSync(join(cwd, \"package-lock.json\"))) return \"npm\"\n\n const userAgent = process.env.npm_config_user_agent || \"\"\n if (userAgent.startsWith(\"pnpm\")) return \"pnpm\"\n if (userAgent.startsWith(\"yarn\")) return \"yarn\"\n if (userAgent.startsWith(\"bun\")) return \"bun\"\n\n return \"npm\"\n}\n","import { cosmiconfig } from \"cosmiconfig\"\nimport { z } from \"zod\"\n\nconst explorer = cosmiconfig(\"sage\", {\n searchPlaces: [\"sage.json\", \"sage.config.json\", \"sage.config.ts\"],\n})\n\nexport const sageConfigSchema = z.object({\n $schema: z.string().optional(),\n framework: z.enum([\"nextjs\", \"tanstack-start\"]).default(\"nextjs\"),\n content: z.string().default(\"content/docs\"),\n agent: z\n .object({\n model: z.string().default(\"anthropic/claude-sonnet-4-6\"),\n provider: z.string().default(\"anthropic\"),\n })\n .default({}),\n ci: z\n .object({\n trigger: z.string().default(\"pr-merge\"),\n workflow: z.string().default(\".github/workflows/sage.yml\"),\n })\n .default({}),\n})\n\nexport type SageConfig = z.infer<typeof sageConfigSchema>\n\nexport async function getConfig(cwd?: string): Promise<SageConfig | null> {\n try {\n const result = await explorer.search(cwd)\n if (!result?.config) return null\n return sageConfigSchema.parse(result.config)\n } catch {\n return null\n }\n}\n\nexport function getDefaultConfig(): SageConfig {\n return sageConfigSchema.parse({})\n}\n","import { existsSync } from \"fs\"\nimport { resolve } from \"path\"\n\nexport type Framework = \"nextjs\" | \"tanstack-start\" | null\n\nexport function detectFramework(cwd: string): Framework {\n if (existsSync(resolve(cwd, \"next.config.js\")) || existsSync(resolve(cwd, \"next.config.ts\")) || existsSync(resolve(cwd, \"next.config.mjs\"))) {\n return \"nextjs\"\n }\n if (existsSync(resolve(cwd, \"vite.config.ts\")) || existsSync(resolve(cwd, \"vite.config.js\"))) {\n const hasRouter = existsSync(resolve(cwd, \"app.config.ts\"))\n if (hasRouter) return \"tanstack-start\"\n }\n return null\n}\n\nexport function detectContentDir(cwd: string): string | null {\n const candidates = [\"content/docs\", \"src/content/docs\"]\n for (const dir of candidates) {\n if (existsSync(resolve(cwd, dir))) {\n return dir\n }\n }\n return null\n}\n","import path from \"path\"\nimport { highlighter } from \"../utils/highlighter.js\"\nimport { logger } from \"../utils/logger.js\"\nimport fg from \"fast-glob\"\nimport fs from \"fs-extra\"\n\nconst FRAMEWORK_CONFIG_FILES = [\n \"next.config.*\",\n \"vite.config.*\",\n \"astro.config.*\",\n \"remix.config.*\",\n \"nuxt.config.*\",\n \"svelte.config.*\",\n \"gatsby-config.*\",\n \"angular.json\",\n]\n\n// Checks for workspace signals at the given directory.\nexport async function isMonorepoRoot(cwd: string) {\n // pnpm workspaces.\n if (fs.existsSync(path.resolve(cwd, \"pnpm-workspace.yaml\"))) {\n return true\n }\n\n // npm/yarn workspaces.\n const packageJsonPath = path.resolve(cwd, \"package.json\")\n if (fs.existsSync(packageJsonPath)) {\n try {\n const packageJson = await fs.readJson(packageJsonPath)\n if (packageJson.workspaces) {\n return true\n }\n } catch {\n // Ignore parse errors.\n }\n }\n\n // Lerna.\n if (fs.existsSync(path.resolve(cwd, \"lerna.json\"))) {\n return true\n }\n\n // Nx.\n if (fs.existsSync(path.resolve(cwd, \"nx.json\"))) {\n return true\n }\n\n return false\n}\n\n// Finds app directories in a monorepo that contain framework configs or sage.json.\nexport async function getMonorepoTargets(cwd: string) {\n const patterns = await getWorkspacePatterns(cwd)\n\n if (!patterns.length) {\n return []\n }\n\n // Resolve patterns to directories.\n const dirs = await fg(patterns, {\n cwd,\n onlyDirectories: true,\n ignore: [\"**/node_modules/**\"],\n })\n\n const targets: { name: string; hasConfig: boolean }[] = []\n\n for (const dir of dirs) {\n const fullPath = path.resolve(cwd, dir)\n\n // Check if it has a package.json (it's an actual workspace).\n if (!fs.existsSync(path.resolve(fullPath, \"package.json\"))) {\n continue\n }\n\n const hasSageJson = fs.existsSync(\n path.resolve(fullPath, \"sage.json\")\n )\n\n // Check for framework config files.\n const hasFrameworkConfig = FRAMEWORK_CONFIG_FILES.some((pattern) => {\n const matches = fg.sync(pattern, {\n cwd: fullPath,\n dot: true,\n })\n return matches.length > 0\n })\n\n if (hasSageJson || hasFrameworkConfig) {\n targets.push({\n name: dir,\n hasConfig: hasSageJson,\n })\n }\n }\n\n return targets\n}\n\n// Formats and logs the monorepo detection message.\nexport function formatMonorepoMessage(\n command: string,\n targets: { name: string; hasConfig: boolean }[],\n options?: {\n cwdFlag?: string\n }\n) {\n const cwdFlag = options?.cwdFlag ?? \"--cwd\"\n\n logger.break()\n logger.log(\n `It looks like you are running ${highlighter.info(\n command\n )} from a monorepo root.`\n )\n logger.log(\n `To use sage in a specific workspace, use the ${highlighter.info(\n cwdFlag\n )} flag:`\n )\n logger.break()\n\n for (const target of targets) {\n logger.log(` sage ${command} ${cwdFlag} ${target.name}`)\n }\n\n logger.break()\n}\n\nexport async function getWorkspacePatterns(cwd: string) {\n const patterns: string[] = []\n\n // Read pnpm-workspace.yaml.\n const pnpmWorkspacePath = path.resolve(cwd, \"pnpm-workspace.yaml\")\n if (fs.existsSync(pnpmWorkspacePath)) {\n const content = await fs.readFile(pnpmWorkspacePath, \"utf8\")\n patterns.push(...parsePnpmWorkspacePackages(content))\n }\n\n // Read package.json workspaces.\n const packageJsonPath = path.resolve(cwd, \"package.json\")\n if (fs.existsSync(packageJsonPath)) {\n try {\n const packageJson = await fs.readJson(packageJsonPath)\n const workspaces = Array.isArray(packageJson.workspaces)\n ? packageJson.workspaces\n : packageJson.workspaces?.packages\n if (Array.isArray(workspaces)) {\n patterns.push(...workspaces.filter((w: string) => !w.startsWith(\"!\")))\n }\n } catch {\n // Ignore parse errors.\n }\n }\n\n return Array.from(new Set(patterns))\n}\n\nexport function parsePnpmWorkspacePackages(content: string) {\n const patterns: string[] = []\n let inPackages = false\n let packagesIndent = 0\n\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim()\n\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue\n }\n\n const keyMatch = line.match(/^(\\s*)([A-Za-z0-9_-]+)\\s*:/)\n if (keyMatch) {\n packagesIndent = keyMatch[1].length\n inPackages = keyMatch[2] === \"packages\"\n continue\n }\n\n if (!inPackages) {\n continue\n }\n\n const itemMatch = line.match(/^(\\s*)-\\s*(.+?)\\s*(?:#.*)?$/)\n if (!itemMatch || itemMatch[1].length <= packagesIndent) {\n continue\n }\n\n patterns.push(itemMatch[2].trim().replace(/^[\"']|[\"']$/g, \"\"))\n }\n\n return patterns\n}\n","import kleur from \"kleur\"\n\nexport const highlighter = {\n error: kleur.red,\n warn: kleur.yellow,\n info: kleur.cyan,\n success: kleur.green,\n highlight: kleur.bold,\n dim: kleur.dim,\n code: kleur.italic,\n}\n","import { Command } from \"commander\";\nimport { resolve } from \"path\";\nimport { z } from \"zod\";\nimport { handleError } from \"../utils/handle-error.js\";\nimport { getConfig } from \"../utils/get-config.js\";\nimport { logger } from \"../utils/logger.js\";\n\nconst devOptionsSchema = z.object({\n cwd: z.string().optional(),\n port: z.coerce.number().optional(),\n});\n\n// Right now it does not do shit apart from just informing user if config is fine, and giving instruction to setup of fumadocs properly\n//\n// Probably here see if fuma-docs is setup properly?? if not setup than give user quick command i.e fuma-docs setup script they can run; otherwise start the fuma-docs server from here;\n//\n// They can directly do that, and this is duplicate but still good to have here\nexport const dev = new Command()\n .name(\"dev\")\n .description(\"start the help center preview server\")\n .option(\"--cwd <cwd>\", \"working directory\")\n .option(\"-p, --port <port>\", \"port to serve on\")\n .action(async (opts) => {\n try {\n const options = devOptionsSchema.parse(opts);\n const cwd = resolve(options.cwd || process.cwd());\n\n const config = await getConfig(cwd);\n if (!config) {\n logger.error(\"No sage.json found. Run sage init first.\");\n process.exit(1);\n }\n\n logger.info(`Content directory: ${config.content}`);\n logger.info(`Framework: ${config.framework}`);\n logger.break();\n logger.info(\"To preview your help center, run your app's dev server:\");\n logger.info(\" npm run dev\");\n logger.break();\n logger.info(\"Make sure Fumadocs is configured in your app to read from:\");\n logger.info(` ${config.content}`);\n } catch (error) {\n handleError(error);\n }\n });\n"],"mappings":"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","names":["Command","resolve","z","text","existsSync","join","join","existsSync","existsSync","kleur","resolve","z","Command","resolve","z","z","Command","resolve","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/logger.ts","../src/utils/spinner.ts","../src/utils/handle-error.ts","../src/utils/update-dependencies.ts","../src/utils/get-package-manager.ts","../src/utils/get-config.ts","../src/utils/get-project-info.ts","../src/utils/get-monorepo-info.ts","../src/utils/highlighter.ts","../src/utils/templates.ts","../src/commands/dev.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport { init } from \"./commands/init.js\";\nimport { dev } from \"./commands/dev.js\";\n\nprocess.on(\"SIGINT\", () => process.exit(0));\nprocess.on(\"SIGTERM\", () => process.exit(0));\n\nasync function main() {\n const program = new Command()\n .name(\"sage\")\n .description(\"AI documentation agent for your codebase\")\n .version(\"0.0.1\");\n\n program.addCommand(init);\n program.addCommand(dev);\n\n program.parse();\n}\n\nmain();\n","import { Command } from \"commander\";\nimport { constants } from \"fs\";\nimport { access, mkdir, writeFile } from \"fs/promises\";\nimport { resolve } from \"path\";\nimport { z } from \"zod\";\nimport { logger } from \"../utils/logger.js\";\nimport { createSpinner } from \"../utils/spinner.js\";\nimport { handleError } from \"../utils/handle-error.js\";\nimport { ensureFlueDeps } from \"../utils/update-dependencies.js\";\nimport { getDefaultConfig } from \"../utils/get-config.js\";\nimport {\n detectContentDir,\n detectFramework,\n} from \"../utils/get-project-info.js\";\nimport {\n formatMonorepoMessage,\n getMonorepoTargets,\n isMonorepoRoot,\n} from \"../utils/get-monorepo-info.js\";\nimport {\n CI_WORKFLOW,\n FLUE_CONFIG,\n INDEX_MDX,\n SAGE_WORKFLOW,\n} from \"../utils/templates.js\";\n\nconst initOptionsSchema = z.object({\n cwd: z.string().optional(),\n defaults: z.boolean().optional(),\n helpCenter: z.boolean().optional(),\n});\n\n// TODO: we need a robust test fixture for both mono-repo, non-monorepo, next, tanstack; shadcn has all figured that out so we can just copy that approach for this; doing it manually is painstaking and always leads to some error cuz i have to do it and i'm lazy\n//\n// DO this before implementing second issue;\nexport const init = new Command()\n .name(\"init\")\n .alias(\"create\")\n .description(\"initialize sage in your project\")\n .argument(\"[dir]\", \"project directory\")\n .option(\"--cwd <cwd>\", \"working directory\")\n .option(\"--defaults\", \"use default configuration without prompting\")\n .option(\"--help-center\", \"add Fumadocs help center\")\n .action(async (dir, opts) => {\n try {\n const options = initOptionsSchema.parse(opts);\n const cwd = resolve(dir || options.cwd || process.cwd());\n\n // pnpm --filter=@sagebase/cli start:dev init ./apps/web\n // {} /Users/bishalneupane/Desktop/myprojects/sagebase/packages/cli/apps/web\n // Relative path does not work, have to pass absolute stuff; maybe fix this?? prioritize maybe the process.cwd more?? that would probably create yet another set of problems right????\n // This worked: bun sage init ~/Desktop/myprojects/sagebase/apps/web\n //\n // init didn't add any cli script into package.json how is the user supposed to run dev now\n // maybe again same npx sage dev command?? right\n //\n //\n // Would it be possible to avoid creating yet anaother config file cuz we're literally drowing in them\n\n const spinner = createSpinner(\"Setting up Sage...\").start();\n\n const framework = detectFramework(cwd);\n if (!framework) {\n // Check if we're in a monorepo root before failing.\n if (await isMonorepoRoot(cwd)) {\n const targets = await getMonorepoTargets(cwd);\n if (targets.length > 0) {\n spinner.stop();\n formatMonorepoMessage(\"init\", targets);\n process.exit(1);\n }\n }\n spinner.fail(\n \"Could not detect framework. Only Next.js and TanStack Start are supported.\",\n );\n process.exit(1);\n }\n\n // TODO: maybe here we could use the default fuma docs cli here instead of trying to do this stuff manaully we could have more options from there; but that means we are getting joined with fuma-docs so there is a bit of trade-off there; we won't be able to control the whole setting up experience\n // how does the better-t-setup does this things well??\n //\n // Better idea is to let the user do the setup themselves directly using the fuma-docs cli and configure everything they want, and we don't deal with all that complexity ourselves and doing that will make ur repo both easy and we'll not be tied to fuma-docs as well;\n const existingContent = detectContentDir(cwd);\n const contentDir = existingContent || \"content/docs\";\n\n const config = getDefaultConfig();\n config.framework = framework;\n config.content = contentDir;\n\n await mkdir(resolve(cwd, \"content\", \"docs\"), { recursive: true });\n\n const indexMdxPath = resolve(cwd, contentDir, \"index.mdx\");\n try {\n await access(indexMdxPath, constants.F_OK);\n } catch {\n await writeFile(indexMdxPath, INDEX_MDX);\n }\n\n // Even in mono repo it adds the workflow to apps/web/.github** is that the right way?? Need answer on that??\n // Scaffold Flue workflow\n // It has to be workflows cuz that's what flue cli expects\n const flueWorkflowsDir = resolve(cwd, \"src\", \"workflows\");\n await mkdir(flueWorkflowsDir, { recursive: true });\n await writeFile(resolve(flueWorkflowsDir, \"sage.ts\"), SAGE_WORKFLOW);\n await writeFile(resolve(cwd, \"flue.config.ts\"), FLUE_CONFIG);\n\n // Scaffold CI workflow\n const workflowsDir = resolve(cwd, \".github\", \"workflows\");\n await mkdir(workflowsDir, { recursive: true });\n await writeFile(resolve(workflowsDir, \"sage.yml\"), CI_WORKFLOW);\n\n await writeFile(\n resolve(cwd, \"sage.json\"),\n JSON.stringify(config, null, 2),\n );\n\n await ensureFlueDeps(cwd);\n\n spinner.succeed(\"Sage initialized!\");\n\n logger.break();\n logger.success(\"Created:\");\n logger.log(` sage.json — configuration`);\n logger.log(` src/workflows/ — Flue agent workflow`);\n logger.log(` ${contentDir}/ — documentation source`);\n logger.log(` .github/workflows/sage.yml — CI workflow`);\n logger.break();\n logger.info(\"Next steps:\");\n logger.log(\" Run sage dev to preview your help center\");\n logger.log(\" Run sage run to generate documentation from code changes\");\n logger.log(\" Edit src/workflows/sage.ts to customize the agent\");\n logger.break();\n\n if (!existingContent) {\n logger.info(\"Created: content/docs/index.mdx — sample article\");\n }\n } catch (error) {\n handleError(error);\n }\n });\n","import kleur from \"kleur\"\n\nexport const logger = {\n error(...args: unknown[]) {\n console.log(kleur.red(\"✗\"), ...args)\n },\n warn(...args: unknown[]) {\n console.log(kleur.yellow(\"⚠\"), ...args)\n },\n info(...args: unknown[]) {\n console.log(kleur.cyan(\"ℹ\"), ...args)\n },\n success(...args: unknown[]) {\n console.log(kleur.green(\"✓\"), ...args)\n },\n log(...args: unknown[]) {\n console.log(...args)\n },\n break() {\n console.log(\"\")\n },\n}\n","import ora from \"ora\"\n\nexport function createSpinner(text: string) {\n const spinner = ora({\n text,\n color: \"cyan\",\n })\n\n return {\n start() {\n spinner.start()\n return this\n },\n succeed(text?: string) {\n spinner.succeed(text)\n return this\n },\n fail(text?: string) {\n spinner.fail(text)\n return this\n },\n stop() {\n spinner.stop()\n return this\n },\n set text(val: string) {\n spinner.text = val\n },\n }\n}\n","import { logger } from \"./logger.js\"\n\nexport function handleError(error: unknown) {\n if (error instanceof Error) {\n logger.error(error.message)\n } else {\n logger.error(String(error))\n }\n\n process.exit(1)\n}\n","import { existsSync, readFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { execa } from \"execa\";\nimport {\n getPackageManager,\n type PackageManager,\n} from \"./get-package-manager.js\";\nimport { createSpinner } from \"./spinner.js\";\n\nconst FLUE_DEPS: string[] = [];\nconst FLUE_DEV_DEPS = [\"@flue/runtime\", \"@flue/cli\", \"valibot\"];\n\nfunction getInstalledDeps(cwd: string): Set<string> {\n try {\n const pkgPath = join(cwd, \"package.json\");\n if (!existsSync(pkgPath)) return new Set();\n\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return new Set([\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ]);\n } catch {\n return new Set();\n }\n}\n\nfunction getMissingDeps(cwd: string): { deps: string[]; devDeps: string[] } {\n const installed = getInstalledDeps(cwd);\n return {\n deps: FLUE_DEPS.filter((dep) => !installed.has(dep)),\n devDeps: FLUE_DEV_DEPS.filter((dep) => !installed.has(dep)),\n };\n}\n\nasync function installWithPackageManager(\n pm: PackageManager,\n deps: string[],\n devDeps: string[],\n cwd: string,\n) {\n if (pm === \"npm\") {\n if (deps.length) await execa(\"npm\", [\"install\", ...deps], { cwd });\n if (devDeps.length)\n await execa(\"npm\", [\"install\", \"-D\", ...devDeps], { cwd });\n } else {\n if (deps.length) await execa(pm, [\"add\", ...deps], { cwd });\n if (devDeps.length) await execa(pm, [\"add\", \"-D\", ...devDeps], { cwd });\n }\n}\n\nexport async function ensureFlueDeps(cwd: string) {\n const { deps, devDeps } = getMissingDeps(cwd);\n const allMissing = [...deps, ...devDeps];\n if (allMissing.length === 0) return;\n\n const spinner = createSpinner(\n `Installing ${allMissing.join(\", \")}...`,\n ).start();\n\n try {\n const pm = await getPackageManager(cwd);\n await installWithPackageManager(pm, deps, devDeps, cwd);\n spinner.succeed();\n } catch (error) {\n spinner.fail(\"Failed to install Flue dependencies\");\n throw error;\n }\n}\n","import { existsSync } from \"fs\"\nimport { join } from \"path\"\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\"\n\nexport async function getPackageManager(\n cwd: string\n): Promise<PackageManager> {\n if (existsSync(join(cwd, \"pnpm-lock.yaml\"))) return \"pnpm\"\n if (existsSync(join(cwd, \"yarn.lock\"))) return \"yarn\"\n if (existsSync(join(cwd, \"bun.lock\"))) return \"bun\"\n if (existsSync(join(cwd, \"package-lock.json\"))) return \"npm\"\n\n const userAgent = process.env.npm_config_user_agent || \"\"\n if (userAgent.startsWith(\"pnpm\")) return \"pnpm\"\n if (userAgent.startsWith(\"yarn\")) return \"yarn\"\n if (userAgent.startsWith(\"bun\")) return \"bun\"\n\n return \"npm\"\n}\n","import { cosmiconfig } from \"cosmiconfig\"\nimport { z } from \"zod\"\n\nconst explorer = cosmiconfig(\"sage\", {\n searchPlaces: [\"sage.json\", \"sage.config.json\", \"sage.config.ts\"],\n})\n\nexport const sageConfigSchema = z.object({\n $schema: z.string().optional(),\n framework: z.enum([\"nextjs\", \"tanstack-start\"]).default(\"nextjs\"),\n content: z.string().default(\"content/docs\"),\n agent: z\n .object({\n model: z.string().default(\"anthropic/claude-sonnet-4-6\"),\n provider: z.string().default(\"anthropic\"),\n })\n .default({}),\n ci: z\n .object({\n trigger: z.string().default(\"pr-merge\"),\n workflow: z.string().default(\".github/workflows/sage.yml\"),\n })\n .default({}),\n})\n\nexport type SageConfig = z.infer<typeof sageConfigSchema>\n\nexport async function getConfig(cwd?: string): Promise<SageConfig | null> {\n try {\n const result = await explorer.search(cwd)\n if (!result?.config) return null\n return sageConfigSchema.parse(result.config)\n } catch {\n return null\n }\n}\n\nexport function getDefaultConfig(): SageConfig {\n return sageConfigSchema.parse({})\n}\n","import { existsSync } from \"fs\"\nimport { resolve } from \"path\"\n\nexport type Framework = \"nextjs\" | \"tanstack-start\" | null\n\nexport function detectFramework(cwd: string): Framework {\n if (existsSync(resolve(cwd, \"next.config.js\")) || existsSync(resolve(cwd, \"next.config.ts\")) || existsSync(resolve(cwd, \"next.config.mjs\"))) {\n return \"nextjs\"\n }\n if (existsSync(resolve(cwd, \"vite.config.ts\")) || existsSync(resolve(cwd, \"vite.config.js\"))) {\n const hasRouter = existsSync(resolve(cwd, \"app.config.ts\"))\n if (hasRouter) return \"tanstack-start\"\n }\n return null\n}\n\nexport function detectContentDir(cwd: string): string | null {\n const candidates = [\"content/docs\", \"src/content/docs\"]\n for (const dir of candidates) {\n if (existsSync(resolve(cwd, dir))) {\n return dir\n }\n }\n return null\n}\n","import path from \"path\"\nimport { highlighter } from \"../utils/highlighter.js\"\nimport { logger } from \"../utils/logger.js\"\nimport fg from \"fast-glob\"\nimport fs from \"fs-extra\"\n\nconst FRAMEWORK_CONFIG_FILES = [\n \"next.config.*\",\n \"vite.config.*\",\n \"astro.config.*\",\n \"remix.config.*\",\n \"nuxt.config.*\",\n \"svelte.config.*\",\n \"gatsby-config.*\",\n \"angular.json\",\n]\n\n// Checks for workspace signals at the given directory.\nexport async function isMonorepoRoot(cwd: string) {\n // pnpm workspaces.\n if (fs.existsSync(path.resolve(cwd, \"pnpm-workspace.yaml\"))) {\n return true\n }\n\n // npm/yarn workspaces.\n const packageJsonPath = path.resolve(cwd, \"package.json\")\n if (fs.existsSync(packageJsonPath)) {\n try {\n const packageJson = await fs.readJson(packageJsonPath)\n if (packageJson.workspaces) {\n return true\n }\n } catch {\n // Ignore parse errors.\n }\n }\n\n // Lerna.\n if (fs.existsSync(path.resolve(cwd, \"lerna.json\"))) {\n return true\n }\n\n // Nx.\n if (fs.existsSync(path.resolve(cwd, \"nx.json\"))) {\n return true\n }\n\n return false\n}\n\n// Finds app directories in a monorepo that contain framework configs or sage.json.\nexport async function getMonorepoTargets(cwd: string) {\n const patterns = await getWorkspacePatterns(cwd)\n\n if (!patterns.length) {\n return []\n }\n\n // Resolve patterns to directories.\n const dirs = await fg(patterns, {\n cwd,\n onlyDirectories: true,\n ignore: [\"**/node_modules/**\"],\n })\n\n const targets: { name: string; hasConfig: boolean }[] = []\n\n for (const dir of dirs) {\n const fullPath = path.resolve(cwd, dir)\n\n // Check if it has a package.json (it's an actual workspace).\n if (!fs.existsSync(path.resolve(fullPath, \"package.json\"))) {\n continue\n }\n\n const hasSageJson = fs.existsSync(\n path.resolve(fullPath, \"sage.json\")\n )\n\n // Check for framework config files.\n const hasFrameworkConfig = FRAMEWORK_CONFIG_FILES.some((pattern) => {\n const matches = fg.sync(pattern, {\n cwd: fullPath,\n dot: true,\n })\n return matches.length > 0\n })\n\n if (hasSageJson || hasFrameworkConfig) {\n targets.push({\n name: dir,\n hasConfig: hasSageJson,\n })\n }\n }\n\n return targets\n}\n\n// Formats and logs the monorepo detection message.\nexport function formatMonorepoMessage(\n command: string,\n targets: { name: string; hasConfig: boolean }[],\n options?: {\n cwdFlag?: string\n }\n) {\n const cwdFlag = options?.cwdFlag ?? \"--cwd\"\n\n logger.break()\n logger.log(\n `It looks like you are running ${highlighter.info(\n command\n )} from a monorepo root.`\n )\n logger.log(\n `To use sage in a specific workspace, use the ${highlighter.info(\n cwdFlag\n )} flag:`\n )\n logger.break()\n\n for (const target of targets) {\n logger.log(` sage ${command} ${cwdFlag} ${target.name}`)\n }\n\n logger.break()\n}\n\nexport async function getWorkspacePatterns(cwd: string) {\n const patterns: string[] = []\n\n // Read pnpm-workspace.yaml.\n const pnpmWorkspacePath = path.resolve(cwd, \"pnpm-workspace.yaml\")\n if (fs.existsSync(pnpmWorkspacePath)) {\n const content = await fs.readFile(pnpmWorkspacePath, \"utf8\")\n patterns.push(...parsePnpmWorkspacePackages(content))\n }\n\n // Read package.json workspaces.\n const packageJsonPath = path.resolve(cwd, \"package.json\")\n if (fs.existsSync(packageJsonPath)) {\n try {\n const packageJson = await fs.readJson(packageJsonPath)\n const workspaces = Array.isArray(packageJson.workspaces)\n ? packageJson.workspaces\n : packageJson.workspaces?.packages\n if (Array.isArray(workspaces)) {\n patterns.push(...workspaces.filter((w: string) => !w.startsWith(\"!\")))\n }\n } catch {\n // Ignore parse errors.\n }\n }\n\n return Array.from(new Set(patterns))\n}\n\nexport function parsePnpmWorkspacePackages(content: string) {\n const patterns: string[] = []\n let inPackages = false\n let packagesIndent = 0\n\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim()\n\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue\n }\n\n const keyMatch = line.match(/^(\\s*)([A-Za-z0-9_-]+)\\s*:/)\n if (keyMatch) {\n packagesIndent = keyMatch[1].length\n inPackages = keyMatch[2] === \"packages\"\n continue\n }\n\n if (!inPackages) {\n continue\n }\n\n const itemMatch = line.match(/^(\\s*)-\\s*(.+?)\\s*(?:#.*)?$/)\n if (!itemMatch || itemMatch[1].length <= packagesIndent) {\n continue\n }\n\n patterns.push(itemMatch[2].trim().replace(/^[\"']|[\"']$/g, \"\"))\n }\n\n return patterns\n}\n","import kleur from \"kleur\"\n\nexport const highlighter = {\n error: kleur.red,\n warn: kleur.yellow,\n info: kleur.cyan,\n success: kleur.green,\n highlight: kleur.bold,\n dim: kleur.dim,\n code: kleur.italic,\n}\n","export const INDEX_MDX = `---\ntitle: Welcome\ndescription: Welcome to our help center\n---\n\n# Welcome\n\nThis help center is maintained by Sage — an AI agent that keeps your documentation accurate as you ship.\n\n## Getting started\n\nReplace this sample content with your own documentation.\n`;\n\nexport const SAGE_WORKFLOW = `import { defineAgent, defineWorkflow } from \"@flue/runtime\";\nimport { local } from \"@flue/runtime/node\";\nimport * as v from \"valibot\";\n\nconst agent = defineAgent(() => ({\n sandbox: local({\n env: {\n GH_TOKEN: process.env.GH_TOKEN,\n },\n }),\n model: process.env.SAGE_MODEL ?? \"anthropic/claude-sonnet-4-6\",\n instructions: \\`You are Sage, an AI documentation agent. Your job is to analyze code changes and update documentation.\n\n## Frontmatter requirements\nEvery MDX file MUST start with valid YAML frontmatter:\n---\ntitle: Page Title\ndescription: Brief description\n---\n\n## Instructions\n1. Analyze the code diff to understand what changed\n2. Create new MDX files for new features/modules\n3. Update existing MDX files when related code changes\n4. Use clear, concise technical writing\n5. Include code examples where appropriate\n\n## Output format\nReturn a JSON array of file updates. ONLY include files that need creating or updating:\n[\n {\n \"path\": \"relative/path/from-content-dir/file.mdx\",\n \"content\": \"---\\\\\\\\ntitle: ...\\\\\\\\ndescription: ...\\\\\\\\n---\\\\\\\\n\\\\\\\\nFull MDX content here\"\n }\n]\\`,\n}));\n\nexport default defineWorkflow({\n agent,\n input: v.object({\n contentDir: v.optional(v.string(), \"content/docs\"),\n diffSinceSha: v.optional(v.string()),\n }),\n output: v.object({\n filesUpdated: v.number(),\n prUrl: v.optional(v.string()),\n }),\n async run({ harness, input, log }) {\n const session = await harness.session();\n const contentDir = input.contentDir;\n\n // 1. Get git diff\n const mergeSha =\n input.diffSinceSha ||\n (await session.shell('git log --merges -1 --format=\"%H\"')).stdout.trim();\n\n const diff = await session.shell(\n \\`git diff \\${mergeSha}..HEAD --diff-filter=ACMR -- .\\`,\n );\n\n if (!diff.stdout.trim()) {\n log.info(\"no code changes detected — nothing to document\");\n return { filesUpdated: 0 };\n }\n\n const changedFiles = (\n await session.shell(\n \\`git diff \\${mergeSha}..HEAD --diff-filter=ACMR --name-only -- .\\`,\n )\n ).stdout\n .trim()\n .split(\"\\\\n\")\n .filter(Boolean);\n\n // 2. Read existing docs\n const existingDocs = (\n await session.shell(\n \\`find \\${contentDir} -name '*.mdx' -type f 2>/dev/null | sort\\`,\n )\n ).stdout\n .trim()\n .split(\"\\\\n\")\n .filter(Boolean);\n\n const existingContent: { path: string; content: string }[] = [];\n for (const doc of existingDocs) {\n const content = (await session.shell(\\`cat \"\\${doc}\" 2>/dev/null\\`)).stdout\n .trim()\n .slice(0, 2000);\n existingContent.push({ path: doc, content });\n }\n\n // 3. Call LLM to analyze diff and determine doc updates\n let existingContext = \"\";\n if (existingContent.length > 0) {\n existingContext =\n \"\\\\n\\\\n## Existing documentation\\\\n\" +\n existingContent\n .map((d) => \\`### \\${d.path}\\\\n\\\\\\`\\\\\\`\\\\\\`mdx\\\\n\\${d.content}\\\\n\\\\\\`\\\\\\`\\\\\\`\\`)\n .join(\"\\\\n\");\n }\n\n const userMessage = \\`Documentation files live in \\\\\\`\\${contentDir}\\\\\\`. Create and update MDX files there.\\\\n\\\\nHere is the code diff:\\\\n\\\\n\\\\\\`\\\\\\`\\\\\\`diff\\\\n\\${diff.stdout.slice(0, 50000)}\\\\n\\\\\\`\\\\\\`\\\\\\`\\\\n\\\\nChanged files:\\\\n\\${changedFiles.map((f) => \\`- \\${f}\\`).join(\"\\\\n\")}\\${existingContext}\\`;\n\n const { data } = await session.prompt(userMessage, {\n result: v.object({\n updates: v.array(\n v.object({\n path: v.string(),\n content: v.string(),\n }),\n ),\n }),\n });\n\n if (data.updates.length === 0) {\n log.info(\"no documentation updates needed\");\n return { filesUpdated: 0 };\n }\n\n // 4. Write MDX files\n for (const update of data.updates) {\n const fullPath = \\`\\${contentDir}/\\${update.path}\\`;\n await harness.fs.writeFile(fullPath, update.content);\n log.info(\"wrote\", { path: fullPath });\n }\n\n // 5. Create PR\n const timestamp = Date.now();\n const branchName = \\`sage/docs-\\${timestamp}\\`;\n\n const branchExists = (\n await session.shell(\\`git branch --list '\\${branchName}'\\`)\n ).stdout.trim();\n\n if (branchExists) {\n await session.shell(\\`git checkout \"\\${branchName}\"\\`);\n } else {\n await session.shell(\\`git checkout -b \"\\${branchName}\"\\`);\n }\n\n await session.shell(\"git add -A\");\n\n const hasChanges = (\n await session.shell(\"git status --porcelain\")\n ).stdout.trim();\n if (!hasChanges) {\n log.info(\"no changes to commit\");\n return { filesUpdated: data.updates.length };\n }\n\n await session.shell(\"git commit -m 'docs: update documentation via Sage'\");\n await session.shell(\\`git push origin \"\\${branchName}\" -u\\`);\n\n const prUrl = (\n await session.shell(\n \\`gh pr create --title \"docs: update documentation via Sage\" --body \"Automated documentation update by Sage.\\\\n\\\\nFiles changed:\\\\n\\${data.updates.map((u: { path: string; content: string }) => \\`- \\\\\\`\\${u.path}\\\\\\`\\`).join(\"\\\\n\")}\"\\`,\n )\n ).stdout.trim();\n\n log.info(\"PR created\", { url: prUrl });\n\n return { filesUpdated: data.updates.length, prUrl };\n },\n});\n`;\n\nexport const FLUE_CONFIG = `import { defineConfig } from \"@flue/cli/config\";\n\nexport default defineConfig({\n target: \"node\",\n});\n`;\n\nexport const CI_WORKFLOW = `name: Sage Documentation\non:\n push:\n branches: [main]\njobs:\n sage:\n runs-on: ubuntu-latest\n permissions:\n contents: write\n pull-requests: write\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n - uses: actions/setup-node@v4\n with:\n node-version: 22\n - run: npm ci\n - name: Run Sage\n env:\n ANTHROPIC_API_KEY: \\${{ secrets.ANTHROPIC_API_KEY }}\n GH_TOKEN: \\${{ secrets.GITHUB_TOKEN }}\n run: npx flue run sage\n`;\n","import { Command } from \"commander\";\nimport { resolve } from \"path\";\nimport { z } from \"zod\";\nimport { handleError } from \"../utils/handle-error.js\";\nimport { getConfig } from \"../utils/get-config.js\";\nimport { logger } from \"../utils/logger.js\";\n\nconst devOptionsSchema = z.object({\n cwd: z.string().optional(),\n port: z.coerce.number().optional(),\n});\n\n// Right now it does not do shit apart from just informing user if config is fine, and giving instruction to setup of fumadocs properly\n//\n// Probably here see if fuma-docs is setup properly?? if not setup than give user quick command i.e fuma-docs setup script they can run; otherwise start the fuma-docs server from here;\n//\n// They can directly do that, and this is duplicate but still good to have here\nexport const dev = new Command()\n .name(\"dev\")\n .description(\"start the help center preview server\")\n .option(\"--cwd <cwd>\", \"working directory\")\n .option(\"-p, --port <port>\", \"port to serve on\")\n .action(async (opts) => {\n try {\n const options = devOptionsSchema.parse(opts);\n const cwd = resolve(options.cwd || process.cwd());\n\n const config = await getConfig(cwd);\n if (!config) {\n logger.error(\"No sage.json found. Run sage init first.\");\n process.exit(1);\n }\n\n logger.info(`Content directory: ${config.content}`);\n logger.info(`Framework: ${config.framework}`);\n logger.break();\n logger.info(\"To preview your help center, run your app's dev server:\");\n logger.info(\" npm run dev\");\n logger.break();\n logger.info(\"Make sure Fumadocs is configured in your app to read from:\");\n logger.info(` ${config.content}`);\n } catch (error) {\n handleError(error);\n }\n });\n"],"mappings":"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","names":["Command","resolve","z","text","existsSync","join","join","existsSync","existsSync","kleur","z","resolve","Command","resolve","z","z","Command","resolve","Command"]}
|