opencode-plugin-team-agreements 0.2.0 → 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.1](https://github.com/jwilger/opencode-plugin-team-agreements/compare/opencode-plugin-team-agreements-v0.2.0...opencode-plugin-team-agreements-v0.2.1) (2026-01-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * Add comprehensive team agreements with project analysis and split document approach ([#16](https://github.com/jwilger/opencode-plugin-team-agreements/issues/16)) ([084edaa](https://github.com/jwilger/opencode-plugin-team-agreements/commit/084edaad210c23d6f25eaa64b09e730ba474d970))
9
+
3
10
  ## [0.2.0](https://github.com/jwilger/opencode-plugin-team-agreements/compare/opencode-plugin-team-agreements-v0.1.4...opencode-plugin-team-agreements-v0.2.0) (2026-01-23)
4
11
 
5
12
 
package/README.md CHANGED
@@ -4,18 +4,57 @@ An [OpenCode](https://opencode.ai) plugin that helps teams of humans and LLM age
4
4
 
5
5
  ## Features
6
6
 
7
- - **Interactive facilitation** - Guides teams through establishing agreements one topic at a time
8
- - **Core topics covered**:
9
- - Storage location for agreements
10
- - Programming languages and their purposes
11
- - Code quality standards
12
- - Commit message conventions
13
- - Integration workflow (branching, PRs, CI)
14
- - Testing requirements
15
- - Amendment process for updating agreements
16
- - **Extensible** - Add custom topics beyond the core set
17
- - **Context injection** - Automatically injects agreements into LLM context at session start and after compaction
18
- - **Topic suggestions** - Users can suggest new standard topics via GitHub issues (using `gh` CLI)
7
+ - **Comprehensive interview** - Covers 7 categories with ~22 topics for thorough team alignment
8
+ - **Smart project analysis** - Detects languages, frameworks, CI, testing, AI tools to tailor questions
9
+ - **Split document approach**:
10
+ - `docs/TEAM_AGREEMENTS.md` - Full reference documentation for humans
11
+ - `AGENTS.md` - Concise rules that LLMs need in context constantly
12
+ - **Interactive facilitation** - Guides teams through one question at a time, allowing skips and pauses
13
+ - **Enforcement setup** - Detects existing tooling and offers to set up automatic enforcement
14
+ - **Context injection** - Automatically injects LLM-relevant rules after compaction
15
+ - **Topic suggestions** - Suggest new standard topics via GitHub issues
16
+
17
+ ## Topics Covered
18
+
19
+ ### 1. Code & Quality
20
+ - Programming Languages & Tech Stack
21
+ - Code Quality Standards
22
+ - Code Review Process
23
+ - Testing Requirements
24
+
25
+ ### 2. Integration & Delivery
26
+ - Version Control & Branching
27
+ - Continuous Integration
28
+ - Deployment & Release
29
+ - Database & Schema Changes
30
+
31
+ ### 3. Operations & QA
32
+ - Security Practices
33
+ - Monitoring & Observability
34
+ - Performance Standards
35
+ - Accessibility & Internationalization
36
+
37
+ ### 4. Documentation & Knowledge
38
+ - Documentation Standards
39
+ - Architecture Decision Records (ADRs)
40
+ - Dependency Management
41
+
42
+ ### 5. AI/LLM Collaboration
43
+ - AI Tools & Policies
44
+ - Autonomy Boundaries
45
+ - AI Code Generation Standards
46
+ - Context & Session Management
47
+ - Human Oversight & Escalation
48
+ - Learning & Improvement
49
+
50
+ ### 6. Team Process
51
+ - Development Methodology
52
+ - Planning & Work Breakdown
53
+ - Communication & Collaboration
54
+
55
+ ### 7. Governance
56
+ - Amendment Process
57
+ - Open-Ended Topics
19
58
 
20
59
  ## Installation
21
60
 
@@ -36,26 +75,56 @@ Run the `/team-agreements` command in OpenCode:
36
75
  /team-agreements
37
76
  ```
38
77
 
39
- The plugin will guide you through establishing agreements for your project. You can also provide context:
78
+ The plugin will first analyze your project and then guide you through establishing agreements. You can also provide context:
40
79
 
41
80
  ```
42
- /team-agreements I want to update our commit message conventions
81
+ /team-agreements I want to update our AI collaboration policies
43
82
  ```
44
83
 
45
84
  ### Options when agreements exist
46
85
 
47
86
  If `docs/TEAM_AGREEMENTS.md` already exists, you'll be presented with options to:
48
87
  - **Review** - Display current agreements
49
- - **Amend** - Modify a specific section
50
- - **Start Over** - Begin fresh (with confirmation)
88
+ - **Amend** - Modify specific sections
89
+ - **Regenerate AGENTS.md** - Re-extract LLM-relevant rules
90
+
91
+ ### The split document approach
92
+
93
+ This plugin creates two files with different purposes:
94
+
95
+ **`docs/TEAM_AGREEMENTS.md`** - Comprehensive documentation including:
96
+ - Complete code standards with examples
97
+ - Full deployment and release procedures
98
+ - On-call and incident processes
99
+ - Meeting cadences and team ceremonies
100
+ - Everything humans need for reference
101
+
102
+ **`AGENTS.md`** - Only rules that affect day-to-day coding:
103
+ - Code quality standards and patterns
104
+ - Testing requirements
105
+ - Commit message format
106
+ - AI autonomy boundaries
107
+ - Escalation triggers
108
+
109
+ This split ensures LLM context isn't bloated with procedures they don't need constantly (deployment steps, post-mortem templates, etc.) while still giving humans complete documentation.
51
110
 
52
111
  ## How it works
53
112
 
54
- 1. **Command registration** - The plugin registers the `/team-agreements` command via OpenCode's config hook
55
- 2. **Interactive conversation** - An LLM guides you through each topic, asking one question at a time
56
- 3. **Document generation** - Creates `docs/TEAM_AGREEMENTS.md` with your agreements
57
- 4. **Auto-injection** - Adds agreements to the `instructions` config so all agents see them
58
- 5. **Compaction handling** - Re-injects agreements after context compaction in long sessions
113
+ 1. **Project analysis** - Detects your tech stack, frameworks, CI/CD, and AI tools
114
+ 2. **Tailored interview** - Highlights relevant topics and suggests skippable ones
115
+ 3. **Interactive conversation** - One question at a time with trade-off discussions
116
+ 4. **Document generation** - Creates both `docs/TEAM_AGREEMENTS.md` and `AGENTS.md`
117
+ 5. **Enforcement detection** - Identifies existing tooling (ESLint, Prettier, Husky, etc.)
118
+ 6. **Enforcement setup** - Offers to configure automatic enforcement where possible
119
+ 7. **Compaction handling** - Re-injects AGENTS.md after context compaction
120
+
121
+ ## Tools provided
122
+
123
+ The plugin registers these tools for LLM use:
124
+
125
+ - `analyze_project` - Detects languages, frameworks, CI, testing, AI tools, and project characteristics
126
+ - `detect_enforcement_mechanisms` - Finds existing linters, formatters, and CI workflows
127
+ - `suggest_team_agreement_topic` - Files GitHub issues for new topic suggestions
59
128
 
60
129
  ## Suggesting new topics
61
130
 
@@ -78,6 +147,9 @@ npm install
78
147
  # Build
79
148
  npm run build
80
149
 
150
+ # Run tests
151
+ npm test
152
+
81
153
  # Watch mode for development
82
154
  npm run dev
83
155
  ```
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,qBAAqB,CAAA;AAavD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAwGlC,CAAA;AAED,eAAe,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,qBAAqB,CAAA;AAevD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAwHlC,CAAA;AAED,eAAe,oBAAoB,CAAA"}
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@
11
11
  * @packageDocumentation
12
12
  */
13
13
  import { tool } from "@opencode-ai/plugin";
14
- import { COMMAND_TEMPLATE, PLUGIN_REPO, isGhAvailable, loadTeamAgreements, buildTopicIssueBody, detectEnforcementMechanisms, formatEnforcementResults, execAsync, } from "./utils.js";
14
+ import { COMMAND_TEMPLATE, PLUGIN_REPO, isGhAvailable, loadTeamAgreements, buildTopicIssueBody, detectEnforcementMechanisms, formatEnforcementResults, analyzeProject, formatProjectAnalysis, execAsync, } from "./utils.js";
15
15
  /**
16
16
  * TeamAgreementsPlugin - Helps teams establish and maintain shared agreements
17
17
  * for human-LLM collaboration on a codebase.
@@ -42,6 +42,20 @@ export const TeamAgreementsPlugin = async (ctx) => {
42
42
  * Tools for team agreements management
43
43
  */
44
44
  tool: {
45
+ /**
46
+ * Analyze the project to detect languages, frameworks, tools, etc.
47
+ */
48
+ analyze_project: tool({
49
+ description: "Analyze the project to detect languages, frameworks, CI/CD, testing, AI tools, " +
50
+ "database, monitoring, and other characteristics. Use this at the start of the " +
51
+ "team agreements interview to tailor questions to the specific project. Returns " +
52
+ "recommendations for which categories to prioritize and which topics may be skippable.",
53
+ args: {},
54
+ async execute() {
55
+ const analysis = await analyzeProject(directory);
56
+ return formatProjectAnalysis(analysis);
57
+ },
58
+ }),
45
59
  /**
46
60
  * Detect existing enforcement mechanisms in the project
47
61
  */
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAe,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAEvD,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,EACxB,SAAS,GACV,MAAM,YAAY,CAAA;AAEnB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAW,KAAK,EAAE,GAAG,EAAE,EAAE;IACxD,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,CAAA;IAEzB,OAAO;QACL;;;;WAIG;QACH,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACvB,mCAAmC;YACnC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,MAAc,CAAC,OAAO,GAAG,EAAE,CAAA;YAC9B,CAAC;YACA,MAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,GAAG;gBAC3C,WAAW,EAAE,iEAAiE;gBAC9E,QAAQ,EAAE,gBAAgB;aAC3B,CAAA;QACH,CAAC;QAED;;WAEG;QACH,IAAI,EAAE;YACJ;;eAEG;YACH,6BAA6B,EAAE,IAAI,CAAC;gBAClC,WAAW,EACT,yFAAyF;oBACzF,iGAAiG;oBACjG,mGAAmG;gBACrG,IAAI,EAAE,EAAE;gBACR,KAAK,CAAC,OAAO;oBACX,MAAM,UAAU,GAAG,MAAM,2BAA2B,CAAC,SAAS,CAAC,CAAA;oBAC/D,OAAO,wBAAwB,CAAC,UAAU,CAAC,CAAA;gBAC7C,CAAC;aACF,CAAC;YAEF;;eAEG;YACH,4BAA4B,EAAE,IAAI,CAAC;gBACjC,WAAW,EACT,iEAAiE;oBACjE,0DAA0D;oBAC1D,wDAAwD;gBAC1D,IAAI,EAAE;oBACJ,UAAU,EAAE,IAAI,CAAC,MAAM;yBACpB,MAAM,EAAE;yBACR,QAAQ,CAAC,mDAAmD,CAAC;oBAChE,WAAW,EAAE,IAAI,CAAC,MAAM;yBACrB,MAAM,EAAE;yBACR,QAAQ,CACP,kFAAkF,CACnF;oBACH,mBAAmB,EAAE,IAAI,CAAC,MAAM;yBAC7B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;yBAC3B,QAAQ,CACP,oFAAoF,CACrF;oBACH,iBAAiB,EAAE,IAAI,CAAC,MAAM;yBAC3B,MAAM,EAAE;yBACR,QAAQ,EAAE;yBACV,QAAQ,CAAC,0EAA0E,CAAC;iBACxF;gBACD,KAAK,CAAC,OAAO,CAAC,IAAI;oBAChB,2BAA2B;oBAC3B,IAAI,CAAC,CAAC,MAAM,aAAa,EAAE,CAAC,EAAE,CAAC;wBAC7B,OAAO,CACL,oDAAoD;4BACpD,yEAAyE;4BACzE,gCAAgC;4BAChC,sBAAsB,WAAW,0CAA0C,CAC5E,CAAA;oBACH,CAAC;oBAED,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAA;oBAEtC,IAAI,CAAC;wBACH,MAAM,KAAK,GAAG,WAAW,IAAI,CAAC,UAAU,EAAE,CAAA;wBAC1C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAChC,2BAA2B,WAAW,cAAc,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,aAAa,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,0CAA0C,CACrL,CAAA;wBACD,OAAO,+BAA+B,MAAM,CAAC,IAAI,EAAE,EAAE,CAAA;oBACvD,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,OAAO,2BAA2B,KAAK,sDAAsD,WAAW,0CAA0C,CAAA;oBACpJ,CAAC;gBACH,CAAC;aACF,CAAC;SACH;QAED;;;;WAIG;QACH,iCAAiC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC1D,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAA;YACtD,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;YACjC,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAED,eAAe,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { mkdir, writeFile, rm } from "fs/promises";
3
3
  import { join } from "path";
4
4
  import { tmpdir } from "os";
5
5
  import { TeamAgreementsPlugin } from "./index.js";
6
- import { fileExists, loadTeamAgreements, formatQuestionsAsMarkdown, buildTopicIssueBody, detectEnforcementMechanisms, formatEnforcementResults, COMMAND_TEMPLATE, PLUGIN_REPO, } from "./utils.js";
6
+ import { fileExists, loadTeamAgreements, formatQuestionsAsMarkdown, buildTopicIssueBody, detectEnforcementMechanisms, formatEnforcementResults, analyzeProject, formatProjectAnalysis, COMMAND_TEMPLATE, PLUGIN_REPO, } from "./utils.js";
7
7
  describe("fileExists", () => {
8
8
  let testDir;
9
9
  beforeEach(async () => {
@@ -119,37 +119,65 @@ describe("COMMAND_TEMPLATE", () => {
119
119
  expect(COMMAND_TEMPLATE).toContain("$ARGUMENTS");
120
120
  expect(COMMAND_TEMPLATE).toContain("## Overview");
121
121
  expect(COMMAND_TEMPLATE).toContain("AGENTS.md");
122
- expect(COMMAND_TEMPLATE).toContain("## Step 1: Analyze Existing Files");
122
+ expect(COMMAND_TEMPLATE).toContain("## Step 1: Analyze Project & Existing Files");
123
123
  expect(COMMAND_TEMPLATE).toContain("## Step 2: Determine the Scenario");
124
- expect(COMMAND_TEMPLATE).toContain("## Step 3: Gather Team Agreements");
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
125
135
  expect(COMMAND_TEMPLATE).toContain("Programming Languages");
126
136
  expect(COMMAND_TEMPLATE).toContain("Code Quality Standards");
127
- expect(COMMAND_TEMPLATE).toContain("Commit Message Conventions");
128
- expect(COMMAND_TEMPLATE).toContain("Integration Workflow");
137
+ expect(COMMAND_TEMPLATE).toContain("Code Review Process");
129
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");
130
143
  expect(COMMAND_TEMPLATE).toContain("Amendment Process");
131
144
  });
132
145
  it("mentions the suggestion tool", () => {
133
146
  expect(COMMAND_TEMPLATE).toContain("suggest_team_agreement_topic");
134
147
  });
148
+ it("mentions the analyze_project tool", () => {
149
+ expect(COMMAND_TEMPLATE).toContain("analyze_project");
150
+ });
135
151
  it("contains enforcement section", () => {
136
152
  expect(COMMAND_TEMPLATE).toContain("detect_enforcement_mechanisms");
137
153
  expect(COMMAND_TEMPLATE).toContain("Enforcement Mechanisms");
138
- expect(COMMAND_TEMPLATE).toContain("Pre-commit Hooks");
139
- expect(COMMAND_TEMPLATE).toContain("CI Workflows");
140
- expect(COMMAND_TEMPLATE).toContain("GitHub Rulesets");
141
- expect(COMMAND_TEMPLATE).toContain("Linting Rules");
154
+ expect(COMMAND_TEMPLATE).toContain("commitlint");
155
+ expect(COMMAND_TEMPLATE).toContain("CI workflows");
156
+ expect(COMMAND_TEMPLATE).toContain("branch protection");
142
157
  });
143
- it("contains intelligent merging section", () => {
144
- expect(COMMAND_TEMPLATE).toContain("## Step 4: Intelligent Merging");
158
+ it("contains merging guidelines section", () => {
159
+ expect(COMMAND_TEMPLATE).toContain("## Step 5: Generate Documents");
160
+ expect(COMMAND_TEMPLATE).toContain("### Merging Guidelines");
145
161
  expect(COMMAND_TEMPLATE).toContain("Preserve existing structure");
146
162
  expect(COMMAND_TEMPLATE).toContain("Avoid duplication");
147
163
  });
148
164
  it("contains CLAUDE.md coordination section", () => {
149
- expect(COMMAND_TEMPLATE).toContain("## Step 5: Handle CLAUDE.md Coordination");
165
+ expect(COMMAND_TEMPLATE).toContain("## Step 6: Handle CLAUDE.md Coordination");
150
166
  expect(COMMAND_TEMPLATE).toContain("@AGENTS.md");
151
167
  expect(COMMAND_TEMPLATE).toContain("Claude-specific");
152
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");
180
+ });
153
181
  });
154
182
  describe("detectEnforcementMechanisms", () => {
155
183
  let testDir;
@@ -422,5 +450,354 @@ describe("TeamAgreementsPlugin", () => {
422
450
  expect(result).toContain("Detected Enforcement Mechanisms");
423
451
  expect(result).toContain("husky");
424
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
+ });
425
802
  });
426
803
  //# sourceMappingURL=index.test.js.map