opencode-plugin-team-agreements 0.1.4 → 0.2.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.
@@ -2,7 +2,8 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
2
  import { mkdir, writeFile, rm } from "fs/promises";
3
3
  import { join } from "path";
4
4
  import { tmpdir } from "os";
5
- import { fileExists, loadTeamAgreements, formatQuestionsAsMarkdown, buildTopicIssueBody, detectEnforcementMechanisms, formatEnforcementResults, COMMAND_TEMPLATE, PLUGIN_REPO, TeamAgreementsPlugin, } from "./index.js";
5
+ import { TeamAgreementsPlugin } from "./index.js";
6
+ import { fileExists, loadTeamAgreements, formatQuestionsAsMarkdown, buildTopicIssueBody, detectEnforcementMechanisms, formatEnforcementResults, analyzeProject, formatProjectAnalysis, COMMAND_TEMPLATE, PLUGIN_REPO, } from "./utils.js";
6
7
  describe("fileExists", () => {
7
8
  let testDir;
8
9
  beforeEach(async () => {
@@ -29,34 +30,42 @@ describe("loadTeamAgreements", () => {
29
30
  let testDir;
30
31
  beforeEach(async () => {
31
32
  testDir = join(tmpdir(), "team-agreements-test-" + Date.now() + "-" + Math.random().toString(36).slice(2));
32
- await mkdir(join(testDir, "docs"), { recursive: true });
33
+ await mkdir(testDir, { recursive: true });
33
34
  });
34
35
  afterEach(async () => {
35
36
  await rm(testDir, { recursive: true, force: true });
36
37
  });
37
- it("returns formatted content when agreements file exists", async () => {
38
+ it("returns formatted content when AGENTS.md exists", async () => {
38
39
  const agreementsContent = "# My Team Agreements\n\nWe agree to be awesome.";
39
- await writeFile(join(testDir, "docs", "TEAM_AGREEMENTS.md"), agreementsContent);
40
+ await writeFile(join(testDir, "AGENTS.md"), agreementsContent);
40
41
  const result = await loadTeamAgreements(testDir);
41
42
  expect(result).not.toBeNull();
42
43
  expect(result).toContain("## Team Agreements");
44
+ expect(result).toContain("from AGENTS.md");
43
45
  expect(result).toContain("The following team agreements are in effect");
44
46
  expect(result).toContain(agreementsContent);
45
47
  });
46
- it("returns null when agreements file does not exist", async () => {
48
+ it("returns formatted content from CLAUDE.md when AGENTS.md does not exist", async () => {
49
+ const agreementsContent = "# Claude Rules\n\nBe helpful.";
50
+ await writeFile(join(testDir, "CLAUDE.md"), agreementsContent);
47
51
  const result = await loadTeamAgreements(testDir);
48
- expect(result).toBeNull();
52
+ expect(result).not.toBeNull();
53
+ expect(result).toContain("## Team Agreements");
54
+ expect(result).toContain("from CLAUDE.md");
55
+ expect(result).toContain(agreementsContent);
56
+ });
57
+ it("prefers AGENTS.md over CLAUDE.md when both exist", async () => {
58
+ await writeFile(join(testDir, "AGENTS.md"), "# AGENTS content");
59
+ await writeFile(join(testDir, "CLAUDE.md"), "# CLAUDE content");
60
+ const result = await loadTeamAgreements(testDir);
61
+ expect(result).not.toBeNull();
62
+ expect(result).toContain("from AGENTS.md");
63
+ expect(result).toContain("# AGENTS content");
64
+ expect(result).not.toContain("# CLAUDE content");
49
65
  });
50
- it("returns null when docs directory does not exist", async () => {
51
- const emptyDir = join(tmpdir(), "empty-test-" + Date.now());
52
- await mkdir(emptyDir, { recursive: true });
53
- try {
54
- const result = await loadTeamAgreements(emptyDir);
55
- expect(result).toBeNull();
56
- }
57
- finally {
58
- await rm(emptyDir, { recursive: true, force: true });
59
- }
66
+ it("returns null when neither file exists", async () => {
67
+ const result = await loadTeamAgreements(testDir);
68
+ expect(result).toBeNull();
60
69
  });
61
70
  });
62
71
  describe("formatQuestionsAsMarkdown", () => {
@@ -108,25 +117,66 @@ describe("buildTopicIssueBody", () => {
108
117
  describe("COMMAND_TEMPLATE", () => {
109
118
  it("contains required sections", () => {
110
119
  expect(COMMAND_TEMPLATE).toContain("$ARGUMENTS");
111
- expect(COMMAND_TEMPLATE).toContain("## Instructions");
112
- expect(COMMAND_TEMPLATE).toContain("Storage Location");
120
+ expect(COMMAND_TEMPLATE).toContain("## Overview");
121
+ expect(COMMAND_TEMPLATE).toContain("AGENTS.md");
122
+ expect(COMMAND_TEMPLATE).toContain("## Step 1: Analyze Project & Existing Files");
123
+ expect(COMMAND_TEMPLATE).toContain("## Step 2: Determine the Scenario");
124
+ expect(COMMAND_TEMPLATE).toContain("## Step 3: Present Categories Based on Analysis");
125
+ expect(COMMAND_TEMPLATE).toContain("## Step 4: Gather Team Agreements");
126
+ // Categories
127
+ expect(COMMAND_TEMPLATE).toContain("CATEGORY 1: CODE & QUALITY");
128
+ expect(COMMAND_TEMPLATE).toContain("CATEGORY 2: INTEGRATION & DELIVERY");
129
+ expect(COMMAND_TEMPLATE).toContain("CATEGORY 3: OPERATIONS & QA");
130
+ expect(COMMAND_TEMPLATE).toContain("CATEGORY 4: DOCUMENTATION & KNOWLEDGE");
131
+ expect(COMMAND_TEMPLATE).toContain("CATEGORY 5: AI/LLM COLLABORATION");
132
+ expect(COMMAND_TEMPLATE).toContain("CATEGORY 6: TEAM PROCESS");
133
+ expect(COMMAND_TEMPLATE).toContain("CATEGORY 7: GOVERNANCE");
134
+ // Topics
113
135
  expect(COMMAND_TEMPLATE).toContain("Programming Languages");
114
136
  expect(COMMAND_TEMPLATE).toContain("Code Quality Standards");
115
- expect(COMMAND_TEMPLATE).toContain("Commit Message Conventions");
116
- expect(COMMAND_TEMPLATE).toContain("Integration Workflow");
137
+ expect(COMMAND_TEMPLATE).toContain("Code Review Process");
117
138
  expect(COMMAND_TEMPLATE).toContain("Testing Requirements");
139
+ expect(COMMAND_TEMPLATE).toContain("Version Control & Branching");
140
+ expect(COMMAND_TEMPLATE).toContain("Security Practices");
141
+ expect(COMMAND_TEMPLATE).toContain("AI Tools & Policies");
142
+ expect(COMMAND_TEMPLATE).toContain("Autonomy Boundaries");
118
143
  expect(COMMAND_TEMPLATE).toContain("Amendment Process");
119
144
  });
120
145
  it("mentions the suggestion tool", () => {
121
146
  expect(COMMAND_TEMPLATE).toContain("suggest_team_agreement_topic");
122
147
  });
148
+ it("mentions the analyze_project tool", () => {
149
+ expect(COMMAND_TEMPLATE).toContain("analyze_project");
150
+ });
123
151
  it("contains enforcement section", () => {
124
152
  expect(COMMAND_TEMPLATE).toContain("detect_enforcement_mechanisms");
125
153
  expect(COMMAND_TEMPLATE).toContain("Enforcement Mechanisms");
126
- expect(COMMAND_TEMPLATE).toContain("Pre-commit Hooks");
127
- expect(COMMAND_TEMPLATE).toContain("CI Workflows");
128
- expect(COMMAND_TEMPLATE).toContain("GitHub Rulesets");
129
- expect(COMMAND_TEMPLATE).toContain("OpenCode Plugin Hooks");
154
+ expect(COMMAND_TEMPLATE).toContain("commitlint");
155
+ expect(COMMAND_TEMPLATE).toContain("CI workflows");
156
+ expect(COMMAND_TEMPLATE).toContain("branch protection");
157
+ });
158
+ it("contains merging guidelines section", () => {
159
+ expect(COMMAND_TEMPLATE).toContain("## Step 5: Generate Documents");
160
+ expect(COMMAND_TEMPLATE).toContain("### Merging Guidelines");
161
+ expect(COMMAND_TEMPLATE).toContain("Preserve existing structure");
162
+ expect(COMMAND_TEMPLATE).toContain("Avoid duplication");
163
+ });
164
+ it("contains CLAUDE.md coordination section", () => {
165
+ expect(COMMAND_TEMPLATE).toContain("## Step 6: Handle CLAUDE.md Coordination");
166
+ expect(COMMAND_TEMPLATE).toContain("@AGENTS.md");
167
+ expect(COMMAND_TEMPLATE).toContain("Claude-specific");
168
+ });
169
+ it("contains AI/LLM collaboration topics", () => {
170
+ expect(COMMAND_TEMPLATE).toContain("AI Tools & Policies");
171
+ expect(COMMAND_TEMPLATE).toContain("Autonomy Boundaries");
172
+ expect(COMMAND_TEMPLATE).toContain("AI Code Generation Standards");
173
+ expect(COMMAND_TEMPLATE).toContain("Context & Session Management");
174
+ expect(COMMAND_TEMPLATE).toContain("Human Oversight & Escalation");
175
+ expect(COMMAND_TEMPLATE).toContain("Learning & Improvement");
176
+ });
177
+ it("contains progress tracking guidance", () => {
178
+ expect(COMMAND_TEMPLATE).toContain("Category X of 7");
179
+ expect(COMMAND_TEMPLATE).toContain("25-40 minutes");
130
180
  });
131
181
  });
132
182
  describe("detectEnforcementMechanisms", () => {
@@ -278,7 +328,7 @@ describe("TeamAgreementsPlugin", () => {
278
328
  let testDir;
279
329
  beforeEach(async () => {
280
330
  testDir = join(tmpdir(), "team-agreements-plugin-test-" + Date.now() + "-" + Math.random().toString(36).slice(2));
281
- await mkdir(join(testDir, "docs"), { recursive: true });
331
+ await mkdir(testDir, { recursive: true });
282
332
  });
283
333
  afterEach(async () => {
284
334
  await rm(testDir, { recursive: true, force: true });
@@ -300,23 +350,10 @@ describe("TeamAgreementsPlugin", () => {
300
350
  expect(config.command["team-agreements"].description).toContain("team agreements");
301
351
  expect(config.command["team-agreements"].template).toBe(COMMAND_TEMPLATE);
302
352
  });
303
- it("adds agreements to instructions when file exists", async () => {
304
- await writeFile(join(testDir, "docs", "TEAM_AGREEMENTS.md"), "# Agreements");
305
- const mockCtx = {
306
- directory: testDir,
307
- client: {},
308
- project: {},
309
- worktree: testDir,
310
- serverUrl: new URL("http://localhost"),
311
- $: {},
312
- };
313
- const hooks = await TeamAgreementsPlugin(mockCtx);
314
- const config = {};
315
- await hooks.config(config);
316
- expect(config.instructions).toBeDefined();
317
- expect(config.instructions).toContain("docs/TEAM_AGREEMENTS.md");
318
- });
319
- it("does not add instructions when agreements file does not exist", async () => {
353
+ it("does not inject instructions (AGENTS.md is auto-loaded by OpenCode)", async () => {
354
+ // Note: We no longer inject AGENTS.md into instructions because OpenCode
355
+ // automatically loads it from the project root
356
+ await writeFile(join(testDir, "AGENTS.md"), "# Agreements");
320
357
  const mockCtx = {
321
358
  directory: testDir,
322
359
  client: {},
@@ -328,28 +365,11 @@ describe("TeamAgreementsPlugin", () => {
328
365
  const hooks = await TeamAgreementsPlugin(mockCtx);
329
366
  const config = {};
330
367
  await hooks.config(config);
368
+ // Config should NOT have instructions added - AGENTS.md is auto-loaded
331
369
  expect(config.instructions).toBeUndefined();
332
370
  });
333
- it("does not duplicate instructions if already present", async () => {
334
- await writeFile(join(testDir, "docs", "TEAM_AGREEMENTS.md"), "# Agreements");
335
- const mockCtx = {
336
- directory: testDir,
337
- client: {},
338
- project: {},
339
- worktree: testDir,
340
- serverUrl: new URL("http://localhost"),
341
- $: {},
342
- };
343
- const hooks = await TeamAgreementsPlugin(mockCtx);
344
- const config = {
345
- instructions: ["docs/TEAM_AGREEMENTS.md", "other-file.md"],
346
- };
347
- await hooks.config(config);
348
- const count = config.instructions.filter((i) => i === "docs/TEAM_AGREEMENTS.md").length;
349
- expect(count).toBe(1);
350
- });
351
- it("provides compaction hook that injects agreements", async () => {
352
- await writeFile(join(testDir, "docs", "TEAM_AGREEMENTS.md"), "# Test Agreements");
371
+ it("provides compaction hook that injects agreements from AGENTS.md", async () => {
372
+ await writeFile(join(testDir, "AGENTS.md"), "# Test Agreements");
353
373
  const mockCtx = {
354
374
  directory: testDir,
355
375
  client: {},
@@ -430,5 +450,354 @@ describe("TeamAgreementsPlugin", () => {
430
450
  expect(result).toContain("Detected Enforcement Mechanisms");
431
451
  expect(result).toContain("husky");
432
452
  });
453
+ it("registers the analyze_project tool", async () => {
454
+ const mockCtx = {
455
+ directory: testDir,
456
+ client: {},
457
+ project: {},
458
+ worktree: testDir,
459
+ serverUrl: new URL("http://localhost"),
460
+ $: {},
461
+ };
462
+ const hooks = await TeamAgreementsPlugin(mockCtx);
463
+ expect(hooks.tool).toBeDefined();
464
+ expect(hooks.tool.analyze_project).toBeDefined();
465
+ expect(hooks.tool.analyze_project.description).toContain("Analyze the project");
466
+ });
467
+ it("analyze_project tool returns formatted results", async () => {
468
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
469
+ dependencies: { react: "^18.0.0", express: "^4.0.0" },
470
+ devDependencies: { typescript: "^5.0.0", vitest: "^1.0.0" }
471
+ }));
472
+ const mockCtx = {
473
+ directory: testDir,
474
+ client: {},
475
+ project: {},
476
+ worktree: testDir,
477
+ serverUrl: new URL("http://localhost"),
478
+ $: {},
479
+ };
480
+ const mockToolContext = {
481
+ sessionID: "test-session",
482
+ messageID: "test-message",
483
+ agent: "test-agent",
484
+ abort: new AbortController().signal,
485
+ metadata: () => { },
486
+ ask: async () => { },
487
+ };
488
+ const hooks = await TeamAgreementsPlugin(mockCtx);
489
+ const result = await hooks.tool.analyze_project.execute({}, mockToolContext);
490
+ expect(result).toContain("Project Analysis Results");
491
+ expect(result).toContain("Languages");
492
+ expect(result).toContain("Typescript");
493
+ expect(result).toContain("Frameworks");
494
+ expect(result).toContain("React");
495
+ });
496
+ });
497
+ describe("analyzeProject", () => {
498
+ let testDir;
499
+ beforeEach(async () => {
500
+ testDir = join(tmpdir(), "analyze-project-test-" + Date.now() + "-" + Math.random().toString(36).slice(2));
501
+ await mkdir(testDir, { recursive: true });
502
+ });
503
+ afterEach(async () => {
504
+ await rm(testDir, { recursive: true, force: true });
505
+ });
506
+ it("returns default analysis for empty project", async () => {
507
+ const result = await analyzeProject(testDir);
508
+ expect(result.languages.typescript).toBe(false);
509
+ expect(result.languages.javascript).toBe(false);
510
+ expect(result.frameworks.react).toBe(false);
511
+ expect(result.ci.githubActions).toBe(false);
512
+ expect(result.aiTools.agentsMd).toBe(false);
513
+ expect(result.recommendations.suggestedCategories).toContain("Code & Quality");
514
+ });
515
+ it("detects TypeScript from package.json", async () => {
516
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
517
+ devDependencies: { typescript: "^5.0.0" }
518
+ }));
519
+ const result = await analyzeProject(testDir);
520
+ expect(result.languages.typescript).toBe(true);
521
+ expect(result.languages.javascript).toBe(true);
522
+ });
523
+ it("detects TypeScript from tsconfig.json", async () => {
524
+ await writeFile(join(testDir, "tsconfig.json"), JSON.stringify({
525
+ compilerOptions: { target: "ES2022" }
526
+ }));
527
+ const result = await analyzeProject(testDir);
528
+ expect(result.languages.typescript).toBe(true);
529
+ });
530
+ it("detects Python from pyproject.toml", async () => {
531
+ await writeFile(join(testDir, "pyproject.toml"), "[project]\nname = 'test'");
532
+ const result = await analyzeProject(testDir);
533
+ expect(result.languages.python).toBe(true);
534
+ });
535
+ it("detects Rust from Cargo.toml", async () => {
536
+ await writeFile(join(testDir, "Cargo.toml"), "[package]\nname = 'test'");
537
+ const result = await analyzeProject(testDir);
538
+ expect(result.languages.rust).toBe(true);
539
+ });
540
+ it("detects Go from go.mod", async () => {
541
+ await writeFile(join(testDir, "go.mod"), "module example.com/test");
542
+ const result = await analyzeProject(testDir);
543
+ expect(result.languages.go).toBe(true);
544
+ });
545
+ it("detects React from package.json", async () => {
546
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
547
+ dependencies: { react: "^18.0.0" }
548
+ }));
549
+ const result = await analyzeProject(testDir);
550
+ expect(result.frameworks.react).toBe(true);
551
+ expect(result.characteristics.hasFrontend).toBe(true);
552
+ });
553
+ it("detects Express from package.json", async () => {
554
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
555
+ dependencies: { express: "^4.0.0" }
556
+ }));
557
+ const result = await analyzeProject(testDir);
558
+ expect(result.frameworks.express).toBe(true);
559
+ expect(result.characteristics.hasBackend).toBe(true);
560
+ expect(result.characteristics.hasApi).toBe(true);
561
+ });
562
+ it("detects Next.js from package.json", async () => {
563
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
564
+ dependencies: { next: "^14.0.0" }
565
+ }));
566
+ const result = await analyzeProject(testDir);
567
+ expect(result.frameworks.nextjs).toBe(true);
568
+ expect(result.characteristics.hasFrontend).toBe(true);
569
+ expect(result.characteristics.hasBackend).toBe(true);
570
+ });
571
+ it("detects GitHub Actions", async () => {
572
+ await mkdir(join(testDir, ".github", "workflows"), { recursive: true });
573
+ const result = await analyzeProject(testDir);
574
+ expect(result.ci.githubActions).toBe(true);
575
+ });
576
+ it("detects GitLab CI", async () => {
577
+ await writeFile(join(testDir, ".gitlab-ci.yml"), "stages:\n - build");
578
+ const result = await analyzeProject(testDir);
579
+ expect(result.ci.gitlabCi).toBe(true);
580
+ });
581
+ it("detects Jest from package.json", async () => {
582
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
583
+ devDependencies: { jest: "^29.0.0" }
584
+ }));
585
+ const result = await analyzeProject(testDir);
586
+ expect(result.testing.jest).toBe(true);
587
+ });
588
+ it("detects Vitest from package.json", async () => {
589
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
590
+ devDependencies: { vitest: "^1.0.0" }
591
+ }));
592
+ const result = await analyzeProject(testDir);
593
+ expect(result.testing.vitest).toBe(true);
594
+ });
595
+ it("detects pytest from conftest.py", async () => {
596
+ await writeFile(join(testDir, "conftest.py"), "# pytest config");
597
+ const result = await analyzeProject(testDir);
598
+ expect(result.testing.pytest).toBe(true);
599
+ });
600
+ it("detects test directory", async () => {
601
+ await mkdir(join(testDir, "tests"), { recursive: true });
602
+ const result = await analyzeProject(testDir);
603
+ expect(result.testing.hasTestDirectory).toBe(true);
604
+ });
605
+ it("detects AGENTS.md", async () => {
606
+ await writeFile(join(testDir, "AGENTS.md"), "# Agent Instructions");
607
+ const result = await analyzeProject(testDir);
608
+ expect(result.aiTools.agentsMd).toBe(true);
609
+ });
610
+ it("detects CLAUDE.md", async () => {
611
+ await writeFile(join(testDir, "CLAUDE.md"), "# Claude Instructions");
612
+ const result = await analyzeProject(testDir);
613
+ expect(result.aiTools.claudeMd).toBe(true);
614
+ });
615
+ it("detects GitHub Copilot instructions", async () => {
616
+ await mkdir(join(testDir, ".github"), { recursive: true });
617
+ await writeFile(join(testDir, ".github", "copilot-instructions.md"), "# Instructions");
618
+ const result = await analyzeProject(testDir);
619
+ expect(result.aiTools.copilotInstructions).toBe(true);
620
+ });
621
+ it("detects Cursor rules", async () => {
622
+ await writeFile(join(testDir, ".cursorrules"), "# Rules");
623
+ const result = await analyzeProject(testDir);
624
+ expect(result.aiTools.cursorRules).toBe(true);
625
+ });
626
+ it("detects OpenCode config", async () => {
627
+ await writeFile(join(testDir, "opencode.json"), '{}');
628
+ const result = await analyzeProject(testDir);
629
+ expect(result.aiTools.openCodeConfig).toBe(true);
630
+ });
631
+ it("detects Prisma", async () => {
632
+ await mkdir(join(testDir, "prisma"), { recursive: true });
633
+ const result = await analyzeProject(testDir);
634
+ expect(result.database.prisma).toBe(true);
635
+ });
636
+ it("detects migrations directory", async () => {
637
+ await mkdir(join(testDir, "migrations"), { recursive: true });
638
+ const result = await analyzeProject(testDir);
639
+ expect(result.database.hasMigrations).toBe(true);
640
+ });
641
+ it("detects Sentry from package.json", async () => {
642
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
643
+ dependencies: { "@sentry/node": "^7.0.0" }
644
+ }));
645
+ const result = await analyzeProject(testDir);
646
+ expect(result.monitoring.sentry).toBe(true);
647
+ });
648
+ it("detects Docker", async () => {
649
+ await writeFile(join(testDir, "Dockerfile"), "FROM node:20");
650
+ const result = await analyzeProject(testDir);
651
+ expect(result.characteristics.hasDocker).toBe(true);
652
+ });
653
+ it("detects monorepo from workspaces", async () => {
654
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
655
+ workspaces: ["packages/*"]
656
+ }));
657
+ const result = await analyzeProject(testDir);
658
+ expect(result.characteristics.isMonorepo).toBe(true);
659
+ });
660
+ it("detects library from package.json exports", async () => {
661
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
662
+ main: "dist/index.js",
663
+ exports: { ".": "./dist/index.js" }
664
+ }));
665
+ const result = await analyzeProject(testDir);
666
+ expect(result.characteristics.isLibrary).toBe(true);
667
+ });
668
+ it("highlights AI collaboration when AI tools detected", async () => {
669
+ await writeFile(join(testDir, "AGENTS.md"), "# Instructions");
670
+ const result = await analyzeProject(testDir);
671
+ expect(result.recommendations.highlightedTopics).toContain("AI/LLM Collaboration");
672
+ });
673
+ it("marks database topics as skippable when no database", async () => {
674
+ const result = await analyzeProject(testDir);
675
+ expect(result.recommendations.skippableTopics).toContain("Database & Schema Changes");
676
+ });
677
+ it("marks a11y as skippable when no frontend", async () => {
678
+ await writeFile(join(testDir, "package.json"), JSON.stringify({
679
+ dependencies: { express: "^4.0.0" }
680
+ }));
681
+ const result = await analyzeProject(testDir);
682
+ expect(result.recommendations.skippableTopics).toContain("Accessibility & Internationalization");
683
+ });
684
+ });
685
+ describe("formatProjectAnalysis", () => {
686
+ it("formats empty analysis", () => {
687
+ const analysis = {
688
+ languages: { typescript: false, javascript: false, python: false, rust: false, go: false, ruby: false, java: false, csharp: false, other: [] },
689
+ frameworks: { react: false, vue: false, angular: false, nextjs: false, express: false, fastapi: false, django: false, rails: false, springBoot: false, other: [] },
690
+ ci: { githubActions: false, gitlabCi: false, circleCi: false, jenkins: false, other: [] },
691
+ testing: { jest: false, vitest: false, mocha: false, pytest: false, rspec: false, goTest: false, hasTestDirectory: false, other: [] },
692
+ aiTools: { agentsMd: false, claudeMd: false, copilotInstructions: false, cursorRules: false, continueConfig: false, openCodeConfig: false },
693
+ database: { prisma: false, sequelize: false, typeorm: false, drizzle: false, sqlalchemy: false, activeRecord: false, hasMigrations: false, other: [] },
694
+ monitoring: { sentry: false, datadog: false, newRelic: false, prometheus: false, other: [] },
695
+ characteristics: { isMonorepo: false, isLibrary: false, hasDocker: false, hasFrontend: false, hasBackend: false, hasApi: false, hasDocs: false },
696
+ recommendations: { suggestedCategories: ["Code & Quality"], highlightedTopics: [], skippableTopics: [] }
697
+ };
698
+ const result = formatProjectAnalysis(analysis);
699
+ expect(result).toContain("Project Analysis Results");
700
+ expect(result).toContain("Recommendations");
701
+ expect(result).toContain("Code & Quality");
702
+ });
703
+ it("formats languages section", () => {
704
+ const analysis = {
705
+ languages: { typescript: true, javascript: true, python: false, rust: false, go: false, ruby: false, java: false, csharp: false, other: [] },
706
+ frameworks: { react: false, vue: false, angular: false, nextjs: false, express: false, fastapi: false, django: false, rails: false, springBoot: false, other: [] },
707
+ ci: { githubActions: false, gitlabCi: false, circleCi: false, jenkins: false, other: [] },
708
+ testing: { jest: false, vitest: false, mocha: false, pytest: false, rspec: false, goTest: false, hasTestDirectory: false, other: [] },
709
+ aiTools: { agentsMd: false, claudeMd: false, copilotInstructions: false, cursorRules: false, continueConfig: false, openCodeConfig: false },
710
+ database: { prisma: false, sequelize: false, typeorm: false, drizzle: false, sqlalchemy: false, activeRecord: false, hasMigrations: false, other: [] },
711
+ monitoring: { sentry: false, datadog: false, newRelic: false, prometheus: false, other: [] },
712
+ characteristics: { isMonorepo: false, isLibrary: false, hasDocker: false, hasFrontend: false, hasBackend: false, hasApi: false, hasDocs: false },
713
+ recommendations: { suggestedCategories: [], highlightedTopics: [], skippableTopics: [] }
714
+ };
715
+ const result = formatProjectAnalysis(analysis);
716
+ expect(result).toContain("### Languages");
717
+ expect(result).toContain("Typescript");
718
+ expect(result).toContain("Javascript");
719
+ });
720
+ it("formats frameworks section", () => {
721
+ const analysis = {
722
+ languages: { typescript: false, javascript: false, python: false, rust: false, go: false, ruby: false, java: false, csharp: false, other: [] },
723
+ frameworks: { react: true, vue: false, angular: false, nextjs: false, express: true, fastapi: false, django: false, rails: false, springBoot: false, other: [] },
724
+ ci: { githubActions: false, gitlabCi: false, circleCi: false, jenkins: false, other: [] },
725
+ testing: { jest: false, vitest: false, mocha: false, pytest: false, rspec: false, goTest: false, hasTestDirectory: false, other: [] },
726
+ aiTools: { agentsMd: false, claudeMd: false, copilotInstructions: false, cursorRules: false, continueConfig: false, openCodeConfig: false },
727
+ database: { prisma: false, sequelize: false, typeorm: false, drizzle: false, sqlalchemy: false, activeRecord: false, hasMigrations: false, other: [] },
728
+ monitoring: { sentry: false, datadog: false, newRelic: false, prometheus: false, other: [] },
729
+ characteristics: { isMonorepo: false, isLibrary: false, hasDocker: false, hasFrontend: false, hasBackend: false, hasApi: false, hasDocs: false },
730
+ recommendations: { suggestedCategories: [], highlightedTopics: [], skippableTopics: [] }
731
+ };
732
+ const result = formatProjectAnalysis(analysis);
733
+ expect(result).toContain("### Frameworks");
734
+ expect(result).toContain("React");
735
+ expect(result).toContain("Express");
736
+ });
737
+ it("formats AI tools section", () => {
738
+ const analysis = {
739
+ languages: { typescript: false, javascript: false, python: false, rust: false, go: false, ruby: false, java: false, csharp: false, other: [] },
740
+ frameworks: { react: false, vue: false, angular: false, nextjs: false, express: false, fastapi: false, django: false, rails: false, springBoot: false, other: [] },
741
+ ci: { githubActions: false, gitlabCi: false, circleCi: false, jenkins: false, other: [] },
742
+ testing: { jest: false, vitest: false, mocha: false, pytest: false, rspec: false, goTest: false, hasTestDirectory: false, other: [] },
743
+ aiTools: { agentsMd: true, claudeMd: false, copilotInstructions: true, cursorRules: false, continueConfig: false, openCodeConfig: false },
744
+ database: { prisma: false, sequelize: false, typeorm: false, drizzle: false, sqlalchemy: false, activeRecord: false, hasMigrations: false, other: [] },
745
+ monitoring: { sentry: false, datadog: false, newRelic: false, prometheus: false, other: [] },
746
+ characteristics: { isMonorepo: false, isLibrary: false, hasDocker: false, hasFrontend: false, hasBackend: false, hasApi: false, hasDocs: false },
747
+ recommendations: { suggestedCategories: [], highlightedTopics: [], skippableTopics: [] }
748
+ };
749
+ const result = formatProjectAnalysis(analysis);
750
+ expect(result).toContain("### AI Tools Configuration");
751
+ expect(result).toContain("AGENTS.md");
752
+ expect(result).toContain("GitHub Copilot instructions");
753
+ });
754
+ it("shows no CI message when not detected", () => {
755
+ const analysis = {
756
+ languages: { typescript: false, javascript: false, python: false, rust: false, go: false, ruby: false, java: false, csharp: false, other: [] },
757
+ frameworks: { react: false, vue: false, angular: false, nextjs: false, express: false, fastapi: false, django: false, rails: false, springBoot: false, other: [] },
758
+ ci: { githubActions: false, gitlabCi: false, circleCi: false, jenkins: false, other: [] },
759
+ testing: { jest: false, vitest: false, mocha: false, pytest: false, rspec: false, goTest: false, hasTestDirectory: false, other: [] },
760
+ aiTools: { agentsMd: false, claudeMd: false, copilotInstructions: false, cursorRules: false, continueConfig: false, openCodeConfig: false },
761
+ database: { prisma: false, sequelize: false, typeorm: false, drizzle: false, sqlalchemy: false, activeRecord: false, hasMigrations: false, other: [] },
762
+ monitoring: { sentry: false, datadog: false, newRelic: false, prometheus: false, other: [] },
763
+ characteristics: { isMonorepo: false, isLibrary: false, hasDocker: false, hasFrontend: false, hasBackend: false, hasApi: false, hasDocs: false },
764
+ recommendations: { suggestedCategories: [], highlightedTopics: [], skippableTopics: [] }
765
+ };
766
+ const result = formatProjectAnalysis(analysis);
767
+ expect(result).toContain("No CI/CD detected");
768
+ });
769
+ it("formats highlighted topics", () => {
770
+ const analysis = {
771
+ languages: { typescript: false, javascript: false, python: false, rust: false, go: false, ruby: false, java: false, csharp: false, other: [] },
772
+ frameworks: { react: false, vue: false, angular: false, nextjs: false, express: false, fastapi: false, django: false, rails: false, springBoot: false, other: [] },
773
+ ci: { githubActions: false, gitlabCi: false, circleCi: false, jenkins: false, other: [] },
774
+ testing: { jest: false, vitest: false, mocha: false, pytest: false, rspec: false, goTest: false, hasTestDirectory: false, other: [] },
775
+ aiTools: { agentsMd: false, claudeMd: false, copilotInstructions: false, cursorRules: false, continueConfig: false, openCodeConfig: false },
776
+ database: { prisma: false, sequelize: false, typeorm: false, drizzle: false, sqlalchemy: false, activeRecord: false, hasMigrations: false, other: [] },
777
+ monitoring: { sentry: false, datadog: false, newRelic: false, prometheus: false, other: [] },
778
+ characteristics: { isMonorepo: false, isLibrary: false, hasDocker: false, hasFrontend: false, hasBackend: false, hasApi: false, hasDocs: false },
779
+ recommendations: { suggestedCategories: [], highlightedTopics: ["AI/LLM Collaboration", "Security"], skippableTopics: [] }
780
+ };
781
+ const result = formatProjectAnalysis(analysis);
782
+ expect(result).toContain("Topics to highlight");
783
+ expect(result).toContain("AI/LLM Collaboration");
784
+ expect(result).toContain("Security");
785
+ });
786
+ it("formats skippable topics", () => {
787
+ const analysis = {
788
+ languages: { typescript: false, javascript: false, python: false, rust: false, go: false, ruby: false, java: false, csharp: false, other: [] },
789
+ frameworks: { react: false, vue: false, angular: false, nextjs: false, express: false, fastapi: false, django: false, rails: false, springBoot: false, other: [] },
790
+ ci: { githubActions: false, gitlabCi: false, circleCi: false, jenkins: false, other: [] },
791
+ testing: { jest: false, vitest: false, mocha: false, pytest: false, rspec: false, goTest: false, hasTestDirectory: false, other: [] },
792
+ aiTools: { agentsMd: false, claudeMd: false, copilotInstructions: false, cursorRules: false, continueConfig: false, openCodeConfig: false },
793
+ database: { prisma: false, sequelize: false, typeorm: false, drizzle: false, sqlalchemy: false, activeRecord: false, hasMigrations: false, other: [] },
794
+ monitoring: { sentry: false, datadog: false, newRelic: false, prometheus: false, other: [] },
795
+ characteristics: { isMonorepo: false, isLibrary: false, hasDocker: false, hasFrontend: false, hasBackend: false, hasApi: false, hasDocs: false },
796
+ recommendations: { suggestedCategories: [], highlightedTopics: [], skippableTopics: ["Database & Schema Changes"] }
797
+ };
798
+ const result = formatProjectAnalysis(analysis);
799
+ expect(result).toContain("Topics that may be skippable");
800
+ expect(result).toContain("Database & Schema Changes");
801
+ });
433
802
  });
434
803
  //# sourceMappingURL=index.test.js.map