@nicnocquee/dataqueue 1.33.0 → 1.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,11 @@
1
1
  import { spawnSync } from 'child_process';
2
- import path from 'path';
2
+ import path3 from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import { readFileSync, existsSync, chmodSync, writeFileSync, mkdirSync } from 'fs';
4
+ import fs, { readFileSync, existsSync, chmodSync, writeFileSync, mkdirSync } from 'fs';
5
+ import readline from 'readline';
6
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
7
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
+ import { z } from 'zod';
5
9
 
6
10
  // src/cli.ts
7
11
  var DEPENDENCIES_TO_ADD = [
@@ -366,7 +370,7 @@ function detectNextJsAndRouter({
366
370
  existsSyncImpl,
367
371
  readFileSyncImpl
368
372
  }) {
369
- const packageJsonPath = path.join(cwd, "package.json");
373
+ const packageJsonPath = path3.join(cwd, "package.json");
370
374
  if (!existsSyncImpl(packageJsonPath)) {
371
375
  throw new Error("package.json not found in current directory.");
372
376
  }
@@ -379,10 +383,10 @@ function detectNextJsAndRouter({
379
383
  "Not a Next.js project. Could not find 'next' in package.json dependencies."
380
384
  );
381
385
  }
382
- const srcDir = path.join(cwd, "src");
386
+ const srcDir = path3.join(cwd, "src");
383
387
  const srcRoot = existsSyncImpl(srcDir) ? "src" : ".";
384
- const appDir = path.join(cwd, srcRoot, "app");
385
- const pagesDir = path.join(cwd, srcRoot, "pages");
388
+ const appDir = path3.join(cwd, srcRoot, "app");
389
+ const pagesDir = path3.join(cwd, srcRoot, "pages");
386
390
  const hasAppDir = existsSyncImpl(appDir);
387
391
  const hasPagesDir = existsSyncImpl(pagesDir);
388
392
  if (!hasAppDir && !hasPagesDir) {
@@ -443,7 +447,7 @@ function createScaffoldFiles({
443
447
  writeFileSyncImpl,
444
448
  chmodSyncImpl
445
449
  }) {
446
- const appRoutePath = path.join(
450
+ const appRoutePath = path3.join(
447
451
  details.cwd,
448
452
  details.srcRoot,
449
453
  "app",
@@ -453,7 +457,7 @@ function createScaffoldFiles({
453
457
  "[[...task]]",
454
458
  "route.ts"
455
459
  );
456
- const pagesRoutePath = path.join(
460
+ const pagesRoutePath = path3.join(
457
461
  details.cwd,
458
462
  details.srcRoot,
459
463
  "pages",
@@ -462,14 +466,14 @@ function createScaffoldFiles({
462
466
  "manage",
463
467
  "[[...task]].ts"
464
468
  );
465
- const queuePath = path.join(
469
+ const queuePath = path3.join(
466
470
  details.cwd,
467
471
  details.srcRoot,
468
472
  "lib",
469
473
  "dataqueue",
470
474
  "queue.ts"
471
475
  );
472
- const cronPath = path.join(details.cwd, "cron.sh");
476
+ const cronPath = path3.join(details.cwd, "cron.sh");
473
477
  if (details.router === "app") {
474
478
  createFileIfMissing({
475
479
  absolutePath: appRoutePath,
@@ -532,7 +536,7 @@ function createFileIfMissing({
532
536
  log(` [skipped] ${logPath} (already exists)`);
533
537
  return;
534
538
  }
535
- mkdirSyncImpl(path.dirname(absolutePath), { recursive: true });
539
+ mkdirSyncImpl(path3.dirname(absolutePath), { recursive: true });
536
540
  writeFileSyncImpl(absolutePath, content);
537
541
  log(` [created] ${logPath}`);
538
542
  }
@@ -568,21 +572,541 @@ function ensureStringMapSection(packageJson, sectionName) {
568
572
  return packageJson[sectionName];
569
573
  }
570
574
  function toRelativePath(cwd, absolutePath) {
571
- const relative = path.relative(cwd, absolutePath);
575
+ const relative = path3.relative(cwd, absolutePath);
572
576
  return relative || ".";
573
577
  }
578
+ var __filename = fileURLToPath(import.meta.url);
579
+ var __dirname = path3.dirname(__filename);
580
+ var SKILL_DIRS = ["dataqueue-core", "dataqueue-advanced", "dataqueue-react"];
581
+ function detectAiTools(cwd, existsSync2 = fs.existsSync) {
582
+ const tools = [];
583
+ const checks = [
584
+ {
585
+ name: "Cursor",
586
+ indicator: ".cursor",
587
+ targetDir: ".cursor/skills"
588
+ },
589
+ {
590
+ name: "Claude Code",
591
+ indicator: ".claude",
592
+ targetDir: ".claude/skills"
593
+ },
594
+ {
595
+ name: "GitHub Copilot",
596
+ indicator: ".github",
597
+ targetDir: ".github/skills"
598
+ }
599
+ ];
600
+ for (const check of checks) {
601
+ if (existsSync2(path3.join(cwd, check.indicator))) {
602
+ tools.push({ name: check.name, targetDir: check.targetDir });
603
+ }
604
+ }
605
+ return tools;
606
+ }
607
+ function runInstallSkills({
608
+ log = console.log,
609
+ error = console.error,
610
+ exit = (code) => process.exit(code),
611
+ cwd = process.cwd(),
612
+ existsSync: existsSync2 = fs.existsSync,
613
+ mkdirSync: mkdirSync2 = fs.mkdirSync,
614
+ copyFileSync = fs.copyFileSync,
615
+ readdirSync = fs.readdirSync,
616
+ skillsSourceDir = path3.join(__dirname, "../ai/skills")
617
+ } = {}) {
618
+ const tools = detectAiTools(cwd, existsSync2);
619
+ if (tools.length === 0) {
620
+ log("No AI tool directories detected (.cursor/, .claude/, .github/).");
621
+ log("Creating .cursor/skills/ as the default target.");
622
+ tools.push({ name: "Cursor", targetDir: ".cursor/skills" });
623
+ }
624
+ let installed = 0;
625
+ for (const tool of tools) {
626
+ log(`
627
+ Installing skills for ${tool.name}...`);
628
+ for (const skillDir of SKILL_DIRS) {
629
+ const srcDir = path3.join(skillsSourceDir, skillDir);
630
+ const destDir = path3.join(cwd, tool.targetDir, skillDir);
631
+ try {
632
+ mkdirSync2(destDir, { recursive: true });
633
+ const files = readdirSync(srcDir);
634
+ for (const file of files) {
635
+ copyFileSync(path3.join(srcDir, file), path3.join(destDir, file));
636
+ }
637
+ log(` \u2713 ${skillDir}`);
638
+ installed++;
639
+ } catch (err) {
640
+ error(` \u2717 Failed to install ${skillDir}:`, err);
641
+ }
642
+ }
643
+ }
644
+ if (installed > 0) {
645
+ log(
646
+ `
647
+ Done! Installed ${installed} skill(s) for ${tools.map((t) => t.name).join(", ")}.`
648
+ );
649
+ } else {
650
+ error("No skills were installed.");
651
+ exit(1);
652
+ }
653
+ }
654
+ var __filename2 = fileURLToPath(import.meta.url);
655
+ var __dirname2 = path3.dirname(__filename2);
656
+ var RULE_FILES = ["basic.md", "advanced.md", "react-dashboard.md"];
657
+ var MARKER_START = "<!-- DATAQUEUE RULES START -->";
658
+ var MARKER_END = "<!-- DATAQUEUE RULES END -->";
659
+ function upsertMarkedSection(filePath, content, deps) {
660
+ const block = `${MARKER_START}
661
+ ${content}
662
+ ${MARKER_END}`;
663
+ if (!deps.existsSync(filePath)) {
664
+ deps.writeFileSync(filePath, block + "\n");
665
+ return;
666
+ }
667
+ const existing = deps.readFileSync(filePath, "utf-8");
668
+ const startIdx = existing.indexOf(MARKER_START);
669
+ const endIdx = existing.indexOf(MARKER_END);
670
+ if (startIdx !== -1 && endIdx !== -1) {
671
+ const before = existing.slice(0, startIdx);
672
+ const after = existing.slice(endIdx + MARKER_END.length);
673
+ deps.writeFileSync(filePath, before + block + after);
674
+ } else {
675
+ deps.writeFileSync(filePath, existing.trimEnd() + "\n\n" + block + "\n");
676
+ }
677
+ }
678
+ function getAllRulesContent(rulesSourceDir, readFileSync2) {
679
+ return RULE_FILES.map(
680
+ (f) => readFileSync2(path3.join(rulesSourceDir, f), "utf-8")
681
+ ).join("\n\n");
682
+ }
683
+ var CLIENTS = {
684
+ "1": {
685
+ label: "Cursor",
686
+ install: (deps) => {
687
+ const rulesDir = path3.join(deps.cwd, ".cursor", "rules");
688
+ deps.mkdirSync(rulesDir, { recursive: true });
689
+ for (const file of RULE_FILES) {
690
+ const src = deps.readFileSync(
691
+ path3.join(deps.rulesSourceDir, file),
692
+ "utf-8"
693
+ );
694
+ const destName = `dataqueue-${file.replace(/\.md$/, ".mdc")}`;
695
+ deps.writeFileSync(path3.join(rulesDir, destName), src);
696
+ deps.log(` \u2713 .cursor/rules/${destName}`);
697
+ }
698
+ }
699
+ },
700
+ "2": {
701
+ label: "Claude Code",
702
+ install: (deps) => {
703
+ const content = getAllRulesContent(
704
+ deps.rulesSourceDir,
705
+ deps.readFileSync
706
+ );
707
+ const filePath = path3.join(deps.cwd, "CLAUDE.md");
708
+ upsertMarkedSection(filePath, content, deps);
709
+ deps.log(` \u2713 CLAUDE.md`);
710
+ }
711
+ },
712
+ "3": {
713
+ label: "AGENTS.md (Codex, Jules, OpenCode)",
714
+ install: (deps) => {
715
+ const content = getAllRulesContent(
716
+ deps.rulesSourceDir,
717
+ deps.readFileSync
718
+ );
719
+ const filePath = path3.join(deps.cwd, "AGENTS.md");
720
+ upsertMarkedSection(filePath, content, deps);
721
+ deps.log(` \u2713 AGENTS.md`);
722
+ }
723
+ },
724
+ "4": {
725
+ label: "GitHub Copilot",
726
+ install: (deps) => {
727
+ const content = getAllRulesContent(
728
+ deps.rulesSourceDir,
729
+ deps.readFileSync
730
+ );
731
+ deps.mkdirSync(path3.join(deps.cwd, ".github"), { recursive: true });
732
+ const filePath = path3.join(
733
+ deps.cwd,
734
+ ".github",
735
+ "copilot-instructions.md"
736
+ );
737
+ upsertMarkedSection(filePath, content, deps);
738
+ deps.log(` \u2713 .github/copilot-instructions.md`);
739
+ }
740
+ },
741
+ "5": {
742
+ label: "Windsurf",
743
+ install: (deps) => {
744
+ const content = getAllRulesContent(
745
+ deps.rulesSourceDir,
746
+ deps.readFileSync
747
+ );
748
+ const filePath = path3.join(deps.cwd, "CONVENTIONS.md");
749
+ upsertMarkedSection(filePath, content, deps);
750
+ deps.log(` \u2713 CONVENTIONS.md`);
751
+ }
752
+ }
753
+ };
754
+ async function runInstallRules({
755
+ log = console.log,
756
+ error = console.error,
757
+ exit = (code) => process.exit(code),
758
+ cwd = process.cwd(),
759
+ readFileSync: readFileSync2 = fs.readFileSync,
760
+ writeFileSync: writeFileSync2 = fs.writeFileSync,
761
+ appendFileSync = fs.appendFileSync,
762
+ mkdirSync: mkdirSync2 = fs.mkdirSync,
763
+ existsSync: existsSync2 = fs.existsSync,
764
+ rulesSourceDir = path3.join(__dirname2, "../ai/rules"),
765
+ selectedClient
766
+ } = {}) {
767
+ log("DataQueue Agent Rules Installer\n");
768
+ log("Select your AI client:\n");
769
+ for (const [key, client2] of Object.entries(CLIENTS)) {
770
+ log(` ${key}) ${client2.label}`);
771
+ }
772
+ log("");
773
+ let choice = selectedClient;
774
+ if (!choice) {
775
+ const rl = readline.createInterface({
776
+ input: process.stdin,
777
+ output: process.stdout
778
+ });
779
+ choice = await new Promise((resolve) => {
780
+ rl.question("Enter choice (1-5): ", (answer) => {
781
+ rl.close();
782
+ resolve(answer.trim());
783
+ });
784
+ });
785
+ }
786
+ const client = CLIENTS[choice];
787
+ if (!client) {
788
+ error(`Invalid choice: "${choice}". Expected 1-5.`);
789
+ exit(1);
790
+ return;
791
+ }
792
+ log(`
793
+ Installing rules for ${client.label}...`);
794
+ try {
795
+ client.install({
796
+ cwd,
797
+ readFileSync: readFileSync2,
798
+ writeFileSync: writeFileSync2,
799
+ appendFileSync,
800
+ mkdirSync: mkdirSync2,
801
+ existsSync: existsSync2,
802
+ log,
803
+ rulesSourceDir
804
+ });
805
+ log("\nDone!");
806
+ } catch (err) {
807
+ error("Failed to install rules:", err);
808
+ exit(1);
809
+ }
810
+ }
811
+ function upsertMcpConfig(filePath, serverKey, serverConfig, deps) {
812
+ let config = {};
813
+ if (deps.existsSync(filePath)) {
814
+ try {
815
+ config = JSON.parse(deps.readFileSync(filePath, "utf-8"));
816
+ } catch {
817
+ config = {};
818
+ }
819
+ }
820
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
821
+ config.mcpServers = {};
822
+ }
823
+ config.mcpServers[serverKey] = serverConfig;
824
+ deps.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n");
825
+ }
826
+ var MCP_SERVER_CONFIG = {
827
+ command: "npx",
828
+ args: ["dataqueue-cli", "mcp"]
829
+ };
830
+ var MCP_CLIENTS = {
831
+ "1": {
832
+ label: "Cursor",
833
+ install: (deps) => {
834
+ const configDir = path3.join(deps.cwd, ".cursor");
835
+ deps.mkdirSync(configDir, { recursive: true });
836
+ const configFile = path3.join(configDir, "mcp.json");
837
+ upsertMcpConfig(configFile, "dataqueue", MCP_SERVER_CONFIG, deps);
838
+ deps.log(` \u2713 .cursor/mcp.json`);
839
+ }
840
+ },
841
+ "2": {
842
+ label: "Claude Code",
843
+ install: (deps) => {
844
+ const configFile = path3.join(deps.cwd, ".mcp.json");
845
+ upsertMcpConfig(configFile, "dataqueue", MCP_SERVER_CONFIG, deps);
846
+ deps.log(` \u2713 .mcp.json`);
847
+ }
848
+ },
849
+ "3": {
850
+ label: "VS Code (Copilot)",
851
+ install: (deps) => {
852
+ const configDir = path3.join(deps.cwd, ".vscode");
853
+ deps.mkdirSync(configDir, { recursive: true });
854
+ const configFile = path3.join(configDir, "mcp.json");
855
+ upsertMcpConfig(configFile, "dataqueue", MCP_SERVER_CONFIG, deps);
856
+ deps.log(` \u2713 .vscode/mcp.json`);
857
+ }
858
+ },
859
+ "4": {
860
+ label: "Windsurf",
861
+ install: (deps) => {
862
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
863
+ const configFile = path3.join(
864
+ homeDir,
865
+ ".codeium",
866
+ "windsurf",
867
+ "mcp_config.json"
868
+ );
869
+ deps.mkdirSync(path3.dirname(configFile), { recursive: true });
870
+ upsertMcpConfig(configFile, "dataqueue", MCP_SERVER_CONFIG, deps);
871
+ deps.log(` \u2713 ~/.codeium/windsurf/mcp_config.json`);
872
+ }
873
+ }
874
+ };
875
+ async function runInstallMcp({
876
+ log = console.log,
877
+ error = console.error,
878
+ exit = (code) => process.exit(code),
879
+ cwd = process.cwd(),
880
+ readFileSync: readFileSync2 = fs.readFileSync,
881
+ writeFileSync: writeFileSync2 = fs.writeFileSync,
882
+ mkdirSync: mkdirSync2 = fs.mkdirSync,
883
+ existsSync: existsSync2 = fs.existsSync,
884
+ selectedClient
885
+ } = {}) {
886
+ log("DataQueue MCP Server Installer\n");
887
+ log("Select your AI client:\n");
888
+ for (const [key, client2] of Object.entries(MCP_CLIENTS)) {
889
+ log(` ${key}) ${client2.label}`);
890
+ }
891
+ log("");
892
+ let choice = selectedClient;
893
+ if (!choice) {
894
+ const rl = readline.createInterface({
895
+ input: process.stdin,
896
+ output: process.stdout
897
+ });
898
+ choice = await new Promise((resolve) => {
899
+ rl.question("Enter choice (1-4): ", (answer) => {
900
+ rl.close();
901
+ resolve(answer.trim());
902
+ });
903
+ });
904
+ }
905
+ const client = MCP_CLIENTS[choice];
906
+ if (!client) {
907
+ error(`Invalid choice: "${choice}". Expected 1-4.`);
908
+ exit(1);
909
+ return;
910
+ }
911
+ log(`
912
+ Installing MCP config for ${client.label}...`);
913
+ try {
914
+ client.install({
915
+ cwd,
916
+ readFileSync: readFileSync2,
917
+ writeFileSync: writeFileSync2,
918
+ mkdirSync: mkdirSync2,
919
+ existsSync: existsSync2,
920
+ log
921
+ });
922
+ log("\nDone! The MCP server will run via: npx dataqueue-cli mcp");
923
+ } catch (err) {
924
+ error("Failed to install MCP config:", err);
925
+ exit(1);
926
+ }
927
+ }
928
+ var __filename3 = fileURLToPath(import.meta.url);
929
+ var __dirname3 = path3.dirname(__filename3);
930
+ function loadDocsContent(docsPath = path3.join(__dirname3, "../ai/docs-content.json")) {
931
+ const raw = fs.readFileSync(docsPath, "utf-8");
932
+ return JSON.parse(raw);
933
+ }
934
+ function scorePageForQuery(page, queryTerms) {
935
+ const titleLower = page.title.toLowerCase();
936
+ const descLower = page.description.toLowerCase();
937
+ const contentLower = page.content.toLowerCase();
938
+ let score = 0;
939
+ for (const term of queryTerms) {
940
+ if (titleLower.includes(term)) score += 10;
941
+ if (descLower.includes(term)) score += 5;
942
+ const contentMatches = contentLower.split(term).length - 1;
943
+ score += Math.min(contentMatches, 10);
944
+ }
945
+ return score;
946
+ }
947
+ function extractExcerpt(content, queryTerms, maxLength = 500) {
948
+ const lower = content.toLowerCase();
949
+ let earliestIndex = -1;
950
+ for (const term of queryTerms) {
951
+ const idx = lower.indexOf(term);
952
+ if (idx !== -1 && (earliestIndex === -1 || idx < earliestIndex)) {
953
+ earliestIndex = idx;
954
+ }
955
+ }
956
+ if (earliestIndex === -1) {
957
+ return content.slice(0, maxLength);
958
+ }
959
+ const start = Math.max(0, earliestIndex - 100);
960
+ const end = Math.min(content.length, start + maxLength);
961
+ let excerpt = content.slice(start, end);
962
+ if (start > 0) excerpt = "..." + excerpt;
963
+ if (end < content.length) excerpt = excerpt + "...";
964
+ return excerpt;
965
+ }
966
+ async function startMcpServer(deps = {}) {
967
+ const pages = loadDocsContent(deps.docsPath);
968
+ const server = new McpServer({
969
+ name: "dataqueue-docs",
970
+ version: "1.0.0"
971
+ });
972
+ server.resource("llms-txt", "dataqueue://llms.txt", async () => {
973
+ const llmsPath = path3.join(
974
+ __dirname3,
975
+ "../ai/skills/dataqueue-core/SKILL.md"
976
+ );
977
+ let content;
978
+ try {
979
+ content = fs.readFileSync(llmsPath, "utf-8");
980
+ } catch {
981
+ content = pages.map((p) => `## ${p.title}
982
+
983
+ Slug: ${p.slug}
984
+
985
+ ${p.description}`).join("\n\n");
986
+ }
987
+ return { contents: [{ uri: "dataqueue://llms.txt", text: content }] };
988
+ });
989
+ server.tool(
990
+ "list-doc-pages",
991
+ "List all available DataQueue documentation pages with titles and descriptions.",
992
+ {},
993
+ async () => {
994
+ const listing = pages.map((p) => ({
995
+ slug: p.slug,
996
+ title: p.title,
997
+ description: p.description
998
+ }));
999
+ return {
1000
+ content: [
1001
+ { type: "text", text: JSON.stringify(listing, null, 2) }
1002
+ ]
1003
+ };
1004
+ }
1005
+ );
1006
+ server.tool(
1007
+ "get-doc-page",
1008
+ "Fetch a specific DataQueue doc page by slug. Returns full page content as markdown.",
1009
+ {
1010
+ slug: z.string().describe('The doc page slug, e.g. "usage/add-job" or "api/job-queue"')
1011
+ },
1012
+ async ({ slug }) => {
1013
+ const page = pages.find((p) => p.slug === slug);
1014
+ if (!page) {
1015
+ return {
1016
+ content: [
1017
+ {
1018
+ type: "text",
1019
+ text: `Page not found: "${slug}". Use list-doc-pages to see available slugs.`
1020
+ }
1021
+ ],
1022
+ isError: true
1023
+ };
1024
+ }
1025
+ const header = page.description ? `# ${page.title}
1026
+
1027
+ > ${page.description}
1028
+
1029
+ ` : `# ${page.title}
1030
+
1031
+ `;
1032
+ return {
1033
+ content: [{ type: "text", text: header + page.content }]
1034
+ };
1035
+ }
1036
+ );
1037
+ server.tool(
1038
+ "search-docs",
1039
+ "Full-text search across all DataQueue documentation pages. Returns matching sections with page titles and content excerpts.",
1040
+ {
1041
+ query: z.string().describe('Search query, e.g. "cron scheduling" or "waitForToken"')
1042
+ },
1043
+ async ({ query }) => {
1044
+ const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 1);
1045
+ if (queryTerms.length === 0) {
1046
+ return {
1047
+ content: [
1048
+ { type: "text", text: "Please provide a search query." }
1049
+ ],
1050
+ isError: true
1051
+ };
1052
+ }
1053
+ const scored = pages.map((page) => ({
1054
+ page,
1055
+ score: scorePageForQuery(page, queryTerms)
1056
+ })).filter((r) => r.score > 0).sort((a, b) => b.score - a.score).slice(0, 5);
1057
+ if (scored.length === 0) {
1058
+ return {
1059
+ content: [
1060
+ {
1061
+ type: "text",
1062
+ text: `No results for "${query}". Try different keywords or use list-doc-pages to browse.`
1063
+ }
1064
+ ]
1065
+ };
1066
+ }
1067
+ const results = scored.map((r) => {
1068
+ const excerpt = extractExcerpt(r.page.content, queryTerms);
1069
+ return `## ${r.page.title} (${r.page.slug})
1070
+
1071
+ ${r.page.description}
1072
+
1073
+ ${excerpt}`;
1074
+ });
1075
+ return {
1076
+ content: [{ type: "text", text: results.join("\n\n---\n\n") }]
1077
+ };
1078
+ }
1079
+ );
1080
+ const transport = deps.transport ?? new StdioServerTransport();
1081
+ await server.connect(transport);
1082
+ return server;
1083
+ }
1084
+ var isDirectRun = process.argv[1] && (process.argv[1].endsWith("/mcp-server.js") || process.argv[1].endsWith("/mcp-server.cjs"));
1085
+ if (isDirectRun) {
1086
+ startMcpServer().catch((err) => {
1087
+ console.error("Failed to start MCP server:", err);
1088
+ process.exit(1);
1089
+ });
1090
+ }
574
1091
 
575
1092
  // src/cli.ts
576
- var __filename = fileURLToPath(import.meta.url);
577
- var __dirname = path.dirname(__filename);
1093
+ var __filename4 = fileURLToPath(import.meta.url);
1094
+ var __dirname4 = path3.dirname(__filename4);
578
1095
  function runCli(argv, {
579
1096
  log = console.log,
580
1097
  error = console.error,
581
1098
  exit = (code) => process.exit(code),
582
1099
  spawnSyncImpl = spawnSync,
583
- migrationsDir = path.join(__dirname, "../migrations"),
1100
+ migrationsDir = path3.join(__dirname4, "../migrations"),
584
1101
  initDeps,
585
- runInitImpl = runInit
1102
+ runInitImpl = runInit,
1103
+ installSkillsDeps,
1104
+ runInstallSkillsImpl = runInstallSkills,
1105
+ installRulesDeps,
1106
+ runInstallRulesImpl = runInstallRules,
1107
+ installMcpDeps,
1108
+ runInstallMcpImpl = runInstallMcp,
1109
+ startMcpServerImpl = startMcpServer
586
1110
  } = {}) {
587
1111
  const [, , command, ...restArgs] = argv;
588
1112
  function printUsage() {
@@ -591,6 +1115,10 @@ function runCli(argv, {
591
1115
  " dataqueue-cli migrate [--envPath <path>] [-s <schema> | --schema <schema>]"
592
1116
  );
593
1117
  log(" dataqueue-cli init");
1118
+ log(" dataqueue-cli install-skills");
1119
+ log(" dataqueue-cli install-rules");
1120
+ log(" dataqueue-cli install-mcp");
1121
+ log(" dataqueue-cli mcp");
594
1122
  log("");
595
1123
  log("Options for migrate:");
596
1124
  log(
@@ -600,24 +1128,13 @@ function runCli(argv, {
600
1128
  " -s, --schema <schema> Set the schema to use (passed to node-pg-migrate)"
601
1129
  );
602
1130
  log("");
603
- log("Notes:");
604
- log(
605
- " - The PG_DATAQUEUE_DATABASE environment variable must be set to your Postgres connection string."
606
- );
607
- log(
608
- " - For managed Postgres (e.g., DigitalOcean) with SSL, set PGSSLMODE=require and PGSSLROOTCERT to your CA .crt file."
609
- );
610
- log(
611
- " Example: PGSSLMODE=require NODE_EXTRA_CA_CERTS=/absolute/path/to/ca.crt PG_DATAQUEUE_DATABASE=... npx dataqueue-cli migrate"
612
- );
613
- log("");
614
- log("Notes for init:");
615
- log(
616
- " - Supports both Next.js App Router and Pages Router (prefers App Router if both exist)."
617
- );
1131
+ log("AI tooling commands:");
1132
+ log(" install-skills Install DataQueue skill files for AI assistants");
1133
+ log(" install-rules Install DataQueue agent rules for AI clients");
618
1134
  log(
619
- " - Scaffolds endpoint, cron.sh, queue placeholder, and package.json entries."
1135
+ " install-mcp Configure the DataQueue MCP server for AI clients"
620
1136
  );
1137
+ log(" mcp Start the DataQueue MCP server (stdio)");
621
1138
  exit(1);
622
1139
  }
623
1140
  if (command === "migrate") {
@@ -661,6 +1178,32 @@ function runCli(argv, {
661
1178
  exit,
662
1179
  ...initDeps
663
1180
  });
1181
+ } else if (command === "install-skills") {
1182
+ runInstallSkillsImpl({
1183
+ log,
1184
+ error,
1185
+ exit,
1186
+ ...installSkillsDeps
1187
+ });
1188
+ } else if (command === "install-rules") {
1189
+ runInstallRulesImpl({
1190
+ log,
1191
+ error,
1192
+ exit,
1193
+ ...installRulesDeps
1194
+ });
1195
+ } else if (command === "install-mcp") {
1196
+ runInstallMcpImpl({
1197
+ log,
1198
+ error,
1199
+ exit,
1200
+ ...installMcpDeps
1201
+ });
1202
+ } else if (command === "mcp") {
1203
+ startMcpServerImpl().catch((err) => {
1204
+ error("Failed to start MCP server:", err);
1205
+ exit(1);
1206
+ });
664
1207
  } else {
665
1208
  printUsage();
666
1209
  }