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.
- package/CHANGELOG.md +23 -0
- package/README.md +93 -21
- package/dist/index.d.ts +15 -46
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -519
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +431 -62
- package/dist/index.test.js.map +1 -1
- package/dist/utils.d.ts +151 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +1613 -0
- package/dist/utils.js.map +1 -0
- package/package.json +1 -1
package/dist/utils.js
ADDED
|
@@ -0,0 +1,1613 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal utility functions for the team-agreements plugin.
|
|
3
|
+
*
|
|
4
|
+
* NOTE: These are intentionally NOT exported from the main module entry point
|
|
5
|
+
* because OpenCode's plugin loader iterates through all exports and tries to
|
|
6
|
+
* call them as plugin functions. Only the plugin function should be exported
|
|
7
|
+
* from the main entry point.
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, access, readdir } from "fs/promises";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import { exec } from "child_process";
|
|
12
|
+
import { promisify } from "util";
|
|
13
|
+
export const execAsync = promisify(exec);
|
|
14
|
+
/**
|
|
15
|
+
* Analyze a project directory to detect languages, frameworks, tools, etc.
|
|
16
|
+
* Used to tailor team agreement questions to the specific project.
|
|
17
|
+
*/
|
|
18
|
+
export async function analyzeProject(directory) {
|
|
19
|
+
const analysis = {
|
|
20
|
+
languages: {
|
|
21
|
+
typescript: false,
|
|
22
|
+
javascript: false,
|
|
23
|
+
python: false,
|
|
24
|
+
rust: false,
|
|
25
|
+
go: false,
|
|
26
|
+
ruby: false,
|
|
27
|
+
java: false,
|
|
28
|
+
csharp: false,
|
|
29
|
+
other: [],
|
|
30
|
+
},
|
|
31
|
+
frameworks: {
|
|
32
|
+
react: false,
|
|
33
|
+
vue: false,
|
|
34
|
+
angular: false,
|
|
35
|
+
nextjs: false,
|
|
36
|
+
express: false,
|
|
37
|
+
fastapi: false,
|
|
38
|
+
django: false,
|
|
39
|
+
rails: false,
|
|
40
|
+
springBoot: false,
|
|
41
|
+
other: [],
|
|
42
|
+
},
|
|
43
|
+
ci: {
|
|
44
|
+
githubActions: false,
|
|
45
|
+
gitlabCi: false,
|
|
46
|
+
circleCi: false,
|
|
47
|
+
jenkins: false,
|
|
48
|
+
other: [],
|
|
49
|
+
},
|
|
50
|
+
testing: {
|
|
51
|
+
jest: false,
|
|
52
|
+
vitest: false,
|
|
53
|
+
mocha: false,
|
|
54
|
+
pytest: false,
|
|
55
|
+
rspec: false,
|
|
56
|
+
goTest: false,
|
|
57
|
+
hasTestDirectory: false,
|
|
58
|
+
other: [],
|
|
59
|
+
},
|
|
60
|
+
aiTools: {
|
|
61
|
+
agentsMd: false,
|
|
62
|
+
claudeMd: false,
|
|
63
|
+
copilotInstructions: false,
|
|
64
|
+
cursorRules: false,
|
|
65
|
+
continueConfig: false,
|
|
66
|
+
openCodeConfig: false,
|
|
67
|
+
},
|
|
68
|
+
database: {
|
|
69
|
+
prisma: false,
|
|
70
|
+
sequelize: false,
|
|
71
|
+
typeorm: false,
|
|
72
|
+
drizzle: false,
|
|
73
|
+
sqlalchemy: false,
|
|
74
|
+
activeRecord: false,
|
|
75
|
+
hasMigrations: false,
|
|
76
|
+
other: [],
|
|
77
|
+
},
|
|
78
|
+
monitoring: {
|
|
79
|
+
sentry: false,
|
|
80
|
+
datadog: false,
|
|
81
|
+
newRelic: false,
|
|
82
|
+
prometheus: false,
|
|
83
|
+
other: [],
|
|
84
|
+
},
|
|
85
|
+
characteristics: {
|
|
86
|
+
isMonorepo: false,
|
|
87
|
+
isLibrary: false,
|
|
88
|
+
hasDocker: false,
|
|
89
|
+
hasFrontend: false,
|
|
90
|
+
hasBackend: false,
|
|
91
|
+
hasApi: false,
|
|
92
|
+
hasDocs: false,
|
|
93
|
+
},
|
|
94
|
+
recommendations: {
|
|
95
|
+
suggestedCategories: [],
|
|
96
|
+
highlightedTopics: [],
|
|
97
|
+
skippableTopics: [],
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
// Check for various config files and directories
|
|
101
|
+
const checks = [
|
|
102
|
+
// Languages
|
|
103
|
+
{
|
|
104
|
+
path: "package.json",
|
|
105
|
+
readContent: true,
|
|
106
|
+
callback: (exists, content) => {
|
|
107
|
+
if (exists && content) {
|
|
108
|
+
analysis.languages.javascript = true;
|
|
109
|
+
try {
|
|
110
|
+
const pkg = JSON.parse(content);
|
|
111
|
+
// Check for TypeScript
|
|
112
|
+
if (pkg.devDependencies?.typescript ||
|
|
113
|
+
pkg.dependencies?.typescript) {
|
|
114
|
+
analysis.languages.typescript = true;
|
|
115
|
+
}
|
|
116
|
+
// Check for frameworks
|
|
117
|
+
if (pkg.dependencies?.react || pkg.devDependencies?.react) {
|
|
118
|
+
analysis.frameworks.react = true;
|
|
119
|
+
analysis.characteristics.hasFrontend = true;
|
|
120
|
+
}
|
|
121
|
+
if (pkg.dependencies?.vue || pkg.devDependencies?.vue) {
|
|
122
|
+
analysis.frameworks.vue = true;
|
|
123
|
+
analysis.characteristics.hasFrontend = true;
|
|
124
|
+
}
|
|
125
|
+
if (pkg.dependencies?.["@angular/core"]) {
|
|
126
|
+
analysis.frameworks.angular = true;
|
|
127
|
+
analysis.characteristics.hasFrontend = true;
|
|
128
|
+
}
|
|
129
|
+
if (pkg.dependencies?.next || pkg.devDependencies?.next) {
|
|
130
|
+
analysis.frameworks.nextjs = true;
|
|
131
|
+
analysis.characteristics.hasFrontend = true;
|
|
132
|
+
analysis.characteristics.hasBackend = true;
|
|
133
|
+
}
|
|
134
|
+
if (pkg.dependencies?.express) {
|
|
135
|
+
analysis.frameworks.express = true;
|
|
136
|
+
analysis.characteristics.hasBackend = true;
|
|
137
|
+
analysis.characteristics.hasApi = true;
|
|
138
|
+
}
|
|
139
|
+
// Check for testing frameworks
|
|
140
|
+
if (pkg.devDependencies?.jest || pkg.dependencies?.jest) {
|
|
141
|
+
analysis.testing.jest = true;
|
|
142
|
+
}
|
|
143
|
+
if (pkg.devDependencies?.vitest || pkg.dependencies?.vitest) {
|
|
144
|
+
analysis.testing.vitest = true;
|
|
145
|
+
}
|
|
146
|
+
if (pkg.devDependencies?.mocha || pkg.dependencies?.mocha) {
|
|
147
|
+
analysis.testing.mocha = true;
|
|
148
|
+
}
|
|
149
|
+
// Check for database ORMs
|
|
150
|
+
if (pkg.dependencies?.prisma || pkg.devDependencies?.prisma) {
|
|
151
|
+
analysis.database.prisma = true;
|
|
152
|
+
}
|
|
153
|
+
if (pkg.dependencies?.sequelize) {
|
|
154
|
+
analysis.database.sequelize = true;
|
|
155
|
+
}
|
|
156
|
+
if (pkg.dependencies?.typeorm) {
|
|
157
|
+
analysis.database.typeorm = true;
|
|
158
|
+
}
|
|
159
|
+
if (pkg.dependencies?.drizzle || pkg.dependencies?.["drizzle-orm"]) {
|
|
160
|
+
analysis.database.drizzle = true;
|
|
161
|
+
}
|
|
162
|
+
// Check for monitoring
|
|
163
|
+
if (pkg.dependencies?.["@sentry/node"] || pkg.dependencies?.["@sentry/react"]) {
|
|
164
|
+
analysis.monitoring.sentry = true;
|
|
165
|
+
}
|
|
166
|
+
if (pkg.dependencies?.["dd-trace"]) {
|
|
167
|
+
analysis.monitoring.datadog = true;
|
|
168
|
+
}
|
|
169
|
+
// Check for monorepo indicators
|
|
170
|
+
if (pkg.workspaces || pkg.devDependencies?.lerna || pkg.devDependencies?.nx) {
|
|
171
|
+
analysis.characteristics.isMonorepo = true;
|
|
172
|
+
}
|
|
173
|
+
// Check if it's a library (has main/exports but no bin)
|
|
174
|
+
if ((pkg.main || pkg.exports) && !pkg.bin) {
|
|
175
|
+
analysis.characteristics.isLibrary = true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// JSON parse failed, still mark as JS project
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
{ path: "tsconfig.json", callback: (e) => { if (e)
|
|
185
|
+
analysis.languages.typescript = true; } },
|
|
186
|
+
{ path: "pyproject.toml", callback: (e) => { if (e)
|
|
187
|
+
analysis.languages.python = true; } },
|
|
188
|
+
{ path: "setup.py", callback: (e) => { if (e)
|
|
189
|
+
analysis.languages.python = true; } },
|
|
190
|
+
{ path: "requirements.txt", callback: (e) => { if (e)
|
|
191
|
+
analysis.languages.python = true; } },
|
|
192
|
+
{ path: "Cargo.toml", callback: (e) => { if (e)
|
|
193
|
+
analysis.languages.rust = true; } },
|
|
194
|
+
{ path: "go.mod", callback: (e) => { if (e)
|
|
195
|
+
analysis.languages.go = true; } },
|
|
196
|
+
{ path: "Gemfile", callback: (e) => { if (e)
|
|
197
|
+
analysis.languages.ruby = true; } },
|
|
198
|
+
{ path: "pom.xml", callback: (e) => { if (e)
|
|
199
|
+
analysis.languages.java = true; } },
|
|
200
|
+
{ path: "build.gradle", callback: (e) => { if (e)
|
|
201
|
+
analysis.languages.java = true; } },
|
|
202
|
+
{ path: "*.csproj", callback: (e) => { if (e)
|
|
203
|
+
analysis.languages.csharp = true; } },
|
|
204
|
+
// CI/CD
|
|
205
|
+
{ path: ".github/workflows", callback: (e) => { if (e)
|
|
206
|
+
analysis.ci.githubActions = true; } },
|
|
207
|
+
{ path: ".gitlab-ci.yml", callback: (e) => { if (e)
|
|
208
|
+
analysis.ci.gitlabCi = true; } },
|
|
209
|
+
{ path: ".circleci", callback: (e) => { if (e)
|
|
210
|
+
analysis.ci.circleCi = true; } },
|
|
211
|
+
{ path: "Jenkinsfile", callback: (e) => { if (e)
|
|
212
|
+
analysis.ci.jenkins = true; } },
|
|
213
|
+
// Testing directories
|
|
214
|
+
{ path: "test", callback: (e) => { if (e)
|
|
215
|
+
analysis.testing.hasTestDirectory = true; } },
|
|
216
|
+
{ path: "tests", callback: (e) => { if (e)
|
|
217
|
+
analysis.testing.hasTestDirectory = true; } },
|
|
218
|
+
{ path: "__tests__", callback: (e) => { if (e)
|
|
219
|
+
analysis.testing.hasTestDirectory = true; } },
|
|
220
|
+
{ path: "spec", callback: (e) => { if (e) {
|
|
221
|
+
analysis.testing.hasTestDirectory = true;
|
|
222
|
+
analysis.testing.rspec = true;
|
|
223
|
+
} } },
|
|
224
|
+
// Python testing
|
|
225
|
+
{ path: "pytest.ini", callback: (e) => { if (e)
|
|
226
|
+
analysis.testing.pytest = true; } },
|
|
227
|
+
{ path: "conftest.py", callback: (e) => { if (e)
|
|
228
|
+
analysis.testing.pytest = true; } },
|
|
229
|
+
// AI Tools
|
|
230
|
+
{ path: "AGENTS.md", callback: (e) => { if (e)
|
|
231
|
+
analysis.aiTools.agentsMd = true; } },
|
|
232
|
+
{ path: "CLAUDE.md", callback: (e) => { if (e)
|
|
233
|
+
analysis.aiTools.claudeMd = true; } },
|
|
234
|
+
{ path: ".github/copilot-instructions.md", callback: (e) => { if (e)
|
|
235
|
+
analysis.aiTools.copilotInstructions = true; } },
|
|
236
|
+
{ path: ".cursorrules", callback: (e) => { if (e)
|
|
237
|
+
analysis.aiTools.cursorRules = true; } },
|
|
238
|
+
{ path: ".cursor/rules", callback: (e) => { if (e)
|
|
239
|
+
analysis.aiTools.cursorRules = true; } },
|
|
240
|
+
{ path: ".continue/config.json", callback: (e) => { if (e)
|
|
241
|
+
analysis.aiTools.continueConfig = true; } },
|
|
242
|
+
{ path: "opencode.json", callback: (e) => { if (e)
|
|
243
|
+
analysis.aiTools.openCodeConfig = true; } },
|
|
244
|
+
// Database
|
|
245
|
+
{ path: "prisma", callback: (e) => { if (e)
|
|
246
|
+
analysis.database.prisma = true; } },
|
|
247
|
+
{ path: "migrations", callback: (e) => { if (e)
|
|
248
|
+
analysis.database.hasMigrations = true; } },
|
|
249
|
+
{ path: "db/migrate", callback: (e) => { if (e) {
|
|
250
|
+
analysis.database.hasMigrations = true;
|
|
251
|
+
analysis.database.activeRecord = true;
|
|
252
|
+
} } },
|
|
253
|
+
{ path: "alembic", callback: (e) => { if (e) {
|
|
254
|
+
analysis.database.hasMigrations = true;
|
|
255
|
+
analysis.database.sqlalchemy = true;
|
|
256
|
+
} } },
|
|
257
|
+
// Monitoring
|
|
258
|
+
{ path: "sentry.properties", callback: (e) => { if (e)
|
|
259
|
+
analysis.monitoring.sentry = true; } },
|
|
260
|
+
{ path: ".sentryclirc", callback: (e) => { if (e)
|
|
261
|
+
analysis.monitoring.sentry = true; } },
|
|
262
|
+
{ path: "datadog.yaml", callback: (e) => { if (e)
|
|
263
|
+
analysis.monitoring.datadog = true; } },
|
|
264
|
+
{ path: "newrelic.js", callback: (e) => { if (e)
|
|
265
|
+
analysis.monitoring.newRelic = true; } },
|
|
266
|
+
{ path: "prometheus.yml", callback: (e) => { if (e)
|
|
267
|
+
analysis.monitoring.prometheus = true; } },
|
|
268
|
+
// Characteristics
|
|
269
|
+
{ path: "Dockerfile", callback: (e) => { if (e)
|
|
270
|
+
analysis.characteristics.hasDocker = true; } },
|
|
271
|
+
{ path: "docker-compose.yml", callback: (e) => { if (e)
|
|
272
|
+
analysis.characteristics.hasDocker = true; } },
|
|
273
|
+
{ path: "docker-compose.yaml", callback: (e) => { if (e)
|
|
274
|
+
analysis.characteristics.hasDocker = true; } },
|
|
275
|
+
{ path: "docs", callback: (e) => { if (e)
|
|
276
|
+
analysis.characteristics.hasDocs = true; } },
|
|
277
|
+
{ path: "documentation", callback: (e) => { if (e)
|
|
278
|
+
analysis.characteristics.hasDocs = true; } },
|
|
279
|
+
// Python frameworks
|
|
280
|
+
{
|
|
281
|
+
path: "pyproject.toml",
|
|
282
|
+
readContent: true,
|
|
283
|
+
callback: (exists, content) => {
|
|
284
|
+
if (exists && content) {
|
|
285
|
+
if (content.includes("fastapi")) {
|
|
286
|
+
analysis.frameworks.fastapi = true;
|
|
287
|
+
analysis.characteristics.hasBackend = true;
|
|
288
|
+
analysis.characteristics.hasApi = true;
|
|
289
|
+
}
|
|
290
|
+
if (content.includes("django")) {
|
|
291
|
+
analysis.frameworks.django = true;
|
|
292
|
+
analysis.characteristics.hasBackend = true;
|
|
293
|
+
}
|
|
294
|
+
if (content.includes("pytest")) {
|
|
295
|
+
analysis.testing.pytest = true;
|
|
296
|
+
}
|
|
297
|
+
if (content.includes("sqlalchemy")) {
|
|
298
|
+
analysis.database.sqlalchemy = true;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
// Ruby frameworks
|
|
304
|
+
{
|
|
305
|
+
path: "Gemfile",
|
|
306
|
+
readContent: true,
|
|
307
|
+
callback: (exists, content) => {
|
|
308
|
+
if (exists && content) {
|
|
309
|
+
if (content.includes("rails")) {
|
|
310
|
+
analysis.frameworks.rails = true;
|
|
311
|
+
analysis.characteristics.hasBackend = true;
|
|
312
|
+
}
|
|
313
|
+
if (content.includes("rspec")) {
|
|
314
|
+
analysis.testing.rspec = true;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
];
|
|
320
|
+
// Run all checks
|
|
321
|
+
for (const check of checks) {
|
|
322
|
+
const fullPath = join(directory, check.path);
|
|
323
|
+
const exists = await fileExists(fullPath);
|
|
324
|
+
if (check.readContent && exists) {
|
|
325
|
+
try {
|
|
326
|
+
const content = await readFile(fullPath, "utf-8");
|
|
327
|
+
check.callback(exists, content);
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
check.callback(exists);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
check.callback(exists);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// Check for frontend indicators in directory listing
|
|
338
|
+
try {
|
|
339
|
+
const entries = await readdir(directory);
|
|
340
|
+
if (entries.includes("src") || entries.includes("app") || entries.includes("pages")) {
|
|
341
|
+
// Could be frontend or backend, need more context
|
|
342
|
+
}
|
|
343
|
+
if (entries.includes("public") || entries.includes("static") || entries.includes("assets")) {
|
|
344
|
+
analysis.characteristics.hasFrontend = true;
|
|
345
|
+
}
|
|
346
|
+
if (entries.includes("api") || entries.includes("routes") || entries.includes("controllers")) {
|
|
347
|
+
analysis.characteristics.hasBackend = true;
|
|
348
|
+
analysis.characteristics.hasApi = true;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
// Directory read failed, continue with other checks
|
|
353
|
+
}
|
|
354
|
+
// Generate recommendations based on analysis
|
|
355
|
+
analysis.recommendations = generateRecommendations(analysis);
|
|
356
|
+
return analysis;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Generate recommendations based on project analysis.
|
|
360
|
+
*/
|
|
361
|
+
function generateRecommendations(analysis) {
|
|
362
|
+
const recommendations = {
|
|
363
|
+
suggestedCategories: [
|
|
364
|
+
"Code & Quality", // Always relevant
|
|
365
|
+
"Integration & Delivery", // Always relevant
|
|
366
|
+
],
|
|
367
|
+
highlightedTopics: [],
|
|
368
|
+
skippableTopics: [],
|
|
369
|
+
};
|
|
370
|
+
// AI Tools category - highlight if AI tools detected
|
|
371
|
+
const hasAiTools = Object.values(analysis.aiTools).some(v => v);
|
|
372
|
+
if (hasAiTools) {
|
|
373
|
+
recommendations.highlightedTopics.push("AI/LLM Collaboration");
|
|
374
|
+
}
|
|
375
|
+
recommendations.suggestedCategories.push("AI/LLM Collaboration");
|
|
376
|
+
// Operations & QA
|
|
377
|
+
const hasMonitoring = Object.entries(analysis.monitoring)
|
|
378
|
+
.filter(([key]) => key !== "other")
|
|
379
|
+
.some(([, value]) => value === true) || analysis.monitoring.other.length > 0;
|
|
380
|
+
if (hasMonitoring) {
|
|
381
|
+
recommendations.highlightedTopics.push("Monitoring & Observability");
|
|
382
|
+
}
|
|
383
|
+
if (analysis.characteristics.hasFrontend) {
|
|
384
|
+
recommendations.highlightedTopics.push("Accessibility");
|
|
385
|
+
recommendations.highlightedTopics.push("Performance Standards");
|
|
386
|
+
}
|
|
387
|
+
recommendations.suggestedCategories.push("Operations & QA");
|
|
388
|
+
// Documentation
|
|
389
|
+
if (analysis.characteristics.isLibrary) {
|
|
390
|
+
recommendations.highlightedTopics.push("Documentation Standards");
|
|
391
|
+
recommendations.highlightedTopics.push("API Documentation");
|
|
392
|
+
}
|
|
393
|
+
recommendations.suggestedCategories.push("Documentation & Knowledge");
|
|
394
|
+
// Team Process
|
|
395
|
+
recommendations.suggestedCategories.push("Team Process");
|
|
396
|
+
// Governance (always)
|
|
397
|
+
recommendations.suggestedCategories.push("Governance");
|
|
398
|
+
// Skippable topics based on what's NOT detected
|
|
399
|
+
const hasDatabase = Object.entries(analysis.database)
|
|
400
|
+
.filter(([key]) => key !== "other")
|
|
401
|
+
.some(([, value]) => value === true) || analysis.database.other.length > 0;
|
|
402
|
+
if (!hasDatabase) {
|
|
403
|
+
recommendations.skippableTopics.push("Database & Schema Changes");
|
|
404
|
+
}
|
|
405
|
+
if (!analysis.characteristics.hasFrontend) {
|
|
406
|
+
recommendations.skippableTopics.push("Accessibility & Internationalization");
|
|
407
|
+
}
|
|
408
|
+
const hasCi = Object.entries(analysis.ci)
|
|
409
|
+
.filter(([key]) => key !== "other")
|
|
410
|
+
.some(([, value]) => value === true) || analysis.ci.other.length > 0;
|
|
411
|
+
if (!hasCi) {
|
|
412
|
+
recommendations.highlightedTopics.push("Continuous Integration (not yet set up)");
|
|
413
|
+
}
|
|
414
|
+
return recommendations;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Format project analysis results as markdown for the LLM.
|
|
418
|
+
*/
|
|
419
|
+
export function formatProjectAnalysis(analysis) {
|
|
420
|
+
const lines = [
|
|
421
|
+
"## Project Analysis Results",
|
|
422
|
+
"",
|
|
423
|
+
"I've analyzed your project and detected the following:",
|
|
424
|
+
"",
|
|
425
|
+
];
|
|
426
|
+
// Languages
|
|
427
|
+
const detectedLanguages = Object.entries(analysis.languages)
|
|
428
|
+
.filter(([key, value]) => key !== "other" && value === true)
|
|
429
|
+
.map(([key]) => key.charAt(0).toUpperCase() + key.slice(1));
|
|
430
|
+
if (analysis.languages.other.length > 0) {
|
|
431
|
+
detectedLanguages.push(...analysis.languages.other);
|
|
432
|
+
}
|
|
433
|
+
if (detectedLanguages.length > 0) {
|
|
434
|
+
lines.push("### Languages");
|
|
435
|
+
lines.push(detectedLanguages.map(l => `- ${l}`).join("\n"));
|
|
436
|
+
lines.push("");
|
|
437
|
+
}
|
|
438
|
+
// Frameworks
|
|
439
|
+
const detectedFrameworks = Object.entries(analysis.frameworks)
|
|
440
|
+
.filter(([key, value]) => key !== "other" && value === true)
|
|
441
|
+
.map(([key]) => {
|
|
442
|
+
const names = {
|
|
443
|
+
react: "React",
|
|
444
|
+
vue: "Vue.js",
|
|
445
|
+
angular: "Angular",
|
|
446
|
+
nextjs: "Next.js",
|
|
447
|
+
express: "Express",
|
|
448
|
+
fastapi: "FastAPI",
|
|
449
|
+
django: "Django",
|
|
450
|
+
rails: "Ruby on Rails",
|
|
451
|
+
springBoot: "Spring Boot",
|
|
452
|
+
};
|
|
453
|
+
return names[key] || key;
|
|
454
|
+
});
|
|
455
|
+
if (analysis.frameworks.other.length > 0) {
|
|
456
|
+
detectedFrameworks.push(...analysis.frameworks.other);
|
|
457
|
+
}
|
|
458
|
+
if (detectedFrameworks.length > 0) {
|
|
459
|
+
lines.push("### Frameworks");
|
|
460
|
+
lines.push(detectedFrameworks.map(f => `- ${f}`).join("\n"));
|
|
461
|
+
lines.push("");
|
|
462
|
+
}
|
|
463
|
+
// CI/CD
|
|
464
|
+
const detectedCi = Object.entries(analysis.ci)
|
|
465
|
+
.filter(([key, value]) => key !== "other" && value === true)
|
|
466
|
+
.map(([key]) => {
|
|
467
|
+
const names = {
|
|
468
|
+
githubActions: "GitHub Actions",
|
|
469
|
+
gitlabCi: "GitLab CI",
|
|
470
|
+
circleCi: "CircleCI",
|
|
471
|
+
jenkins: "Jenkins",
|
|
472
|
+
};
|
|
473
|
+
return names[key] || key;
|
|
474
|
+
});
|
|
475
|
+
if (detectedCi.length > 0) {
|
|
476
|
+
lines.push("### CI/CD");
|
|
477
|
+
lines.push(detectedCi.map(c => `- ${c}`).join("\n"));
|
|
478
|
+
lines.push("");
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
lines.push("### CI/CD");
|
|
482
|
+
lines.push("- No CI/CD detected (consider setting up)");
|
|
483
|
+
lines.push("");
|
|
484
|
+
}
|
|
485
|
+
// Testing
|
|
486
|
+
const detectedTesting = Object.entries(analysis.testing)
|
|
487
|
+
.filter(([key, value]) => key !== "other" && key !== "hasTestDirectory" && value === true)
|
|
488
|
+
.map(([key]) => {
|
|
489
|
+
const names = {
|
|
490
|
+
jest: "Jest",
|
|
491
|
+
vitest: "Vitest",
|
|
492
|
+
mocha: "Mocha",
|
|
493
|
+
pytest: "pytest",
|
|
494
|
+
rspec: "RSpec",
|
|
495
|
+
goTest: "Go test",
|
|
496
|
+
};
|
|
497
|
+
return names[key] || key;
|
|
498
|
+
});
|
|
499
|
+
if (detectedTesting.length > 0 || analysis.testing.hasTestDirectory) {
|
|
500
|
+
lines.push("### Testing");
|
|
501
|
+
if (detectedTesting.length > 0) {
|
|
502
|
+
lines.push(detectedTesting.map(t => `- ${t}`).join("\n"));
|
|
503
|
+
}
|
|
504
|
+
if (analysis.testing.hasTestDirectory) {
|
|
505
|
+
lines.push("- Test directory detected");
|
|
506
|
+
}
|
|
507
|
+
lines.push("");
|
|
508
|
+
}
|
|
509
|
+
// AI Tools
|
|
510
|
+
const detectedAiTools = [];
|
|
511
|
+
if (analysis.aiTools.agentsMd)
|
|
512
|
+
detectedAiTools.push("AGENTS.md");
|
|
513
|
+
if (analysis.aiTools.claudeMd)
|
|
514
|
+
detectedAiTools.push("CLAUDE.md");
|
|
515
|
+
if (analysis.aiTools.copilotInstructions)
|
|
516
|
+
detectedAiTools.push("GitHub Copilot instructions");
|
|
517
|
+
if (analysis.aiTools.cursorRules)
|
|
518
|
+
detectedAiTools.push("Cursor rules");
|
|
519
|
+
if (analysis.aiTools.continueConfig)
|
|
520
|
+
detectedAiTools.push("Continue config");
|
|
521
|
+
if (analysis.aiTools.openCodeConfig)
|
|
522
|
+
detectedAiTools.push("OpenCode config");
|
|
523
|
+
if (detectedAiTools.length > 0) {
|
|
524
|
+
lines.push("### AI Tools Configuration");
|
|
525
|
+
lines.push(detectedAiTools.map(t => `- ${t}`).join("\n"));
|
|
526
|
+
lines.push("");
|
|
527
|
+
}
|
|
528
|
+
// Database
|
|
529
|
+
const detectedDatabase = [];
|
|
530
|
+
if (analysis.database.prisma)
|
|
531
|
+
detectedDatabase.push("Prisma");
|
|
532
|
+
if (analysis.database.sequelize)
|
|
533
|
+
detectedDatabase.push("Sequelize");
|
|
534
|
+
if (analysis.database.typeorm)
|
|
535
|
+
detectedDatabase.push("TypeORM");
|
|
536
|
+
if (analysis.database.drizzle)
|
|
537
|
+
detectedDatabase.push("Drizzle");
|
|
538
|
+
if (analysis.database.sqlalchemy)
|
|
539
|
+
detectedDatabase.push("SQLAlchemy");
|
|
540
|
+
if (analysis.database.activeRecord)
|
|
541
|
+
detectedDatabase.push("Active Record");
|
|
542
|
+
if (analysis.database.hasMigrations && detectedDatabase.length === 0) {
|
|
543
|
+
detectedDatabase.push("Migrations detected");
|
|
544
|
+
}
|
|
545
|
+
if (detectedDatabase.length > 0) {
|
|
546
|
+
lines.push("### Database");
|
|
547
|
+
lines.push(detectedDatabase.map(d => `- ${d}`).join("\n"));
|
|
548
|
+
lines.push("");
|
|
549
|
+
}
|
|
550
|
+
// Monitoring
|
|
551
|
+
const detectedMonitoring = [];
|
|
552
|
+
if (analysis.monitoring.sentry)
|
|
553
|
+
detectedMonitoring.push("Sentry");
|
|
554
|
+
if (analysis.monitoring.datadog)
|
|
555
|
+
detectedMonitoring.push("Datadog");
|
|
556
|
+
if (analysis.monitoring.newRelic)
|
|
557
|
+
detectedMonitoring.push("New Relic");
|
|
558
|
+
if (analysis.monitoring.prometheus)
|
|
559
|
+
detectedMonitoring.push("Prometheus");
|
|
560
|
+
if (detectedMonitoring.length > 0) {
|
|
561
|
+
lines.push("### Monitoring");
|
|
562
|
+
lines.push(detectedMonitoring.map(m => `- ${m}`).join("\n"));
|
|
563
|
+
lines.push("");
|
|
564
|
+
}
|
|
565
|
+
// Project Characteristics
|
|
566
|
+
const characteristics = [];
|
|
567
|
+
if (analysis.characteristics.isMonorepo)
|
|
568
|
+
characteristics.push("Monorepo");
|
|
569
|
+
if (analysis.characteristics.isLibrary)
|
|
570
|
+
characteristics.push("Library/Package");
|
|
571
|
+
if (analysis.characteristics.hasDocker)
|
|
572
|
+
characteristics.push("Docker");
|
|
573
|
+
if (analysis.characteristics.hasFrontend)
|
|
574
|
+
characteristics.push("Frontend");
|
|
575
|
+
if (analysis.characteristics.hasBackend)
|
|
576
|
+
characteristics.push("Backend");
|
|
577
|
+
if (analysis.characteristics.hasApi)
|
|
578
|
+
characteristics.push("API");
|
|
579
|
+
if (analysis.characteristics.hasDocs)
|
|
580
|
+
characteristics.push("Documentation folder");
|
|
581
|
+
if (characteristics.length > 0) {
|
|
582
|
+
lines.push("### Project Characteristics");
|
|
583
|
+
lines.push(characteristics.map(c => `- ${c}`).join("\n"));
|
|
584
|
+
lines.push("");
|
|
585
|
+
}
|
|
586
|
+
// Recommendations
|
|
587
|
+
lines.push("### Recommendations");
|
|
588
|
+
lines.push("");
|
|
589
|
+
lines.push("**Suggested categories to discuss:**");
|
|
590
|
+
lines.push(analysis.recommendations.suggestedCategories.map(c => `- ${c}`).join("\n"));
|
|
591
|
+
lines.push("");
|
|
592
|
+
if (analysis.recommendations.highlightedTopics.length > 0) {
|
|
593
|
+
lines.push("**Topics to highlight (particularly relevant to this project):**");
|
|
594
|
+
lines.push(analysis.recommendations.highlightedTopics.map(t => `- ${t}`).join("\n"));
|
|
595
|
+
lines.push("");
|
|
596
|
+
}
|
|
597
|
+
if (analysis.recommendations.skippableTopics.length > 0) {
|
|
598
|
+
lines.push("**Topics that may be skippable (not detected in project):**");
|
|
599
|
+
lines.push(analysis.recommendations.skippableTopics.map(t => `- ${t}`).join("\n"));
|
|
600
|
+
lines.push("");
|
|
601
|
+
}
|
|
602
|
+
return lines.join("\n");
|
|
603
|
+
}
|
|
604
|
+
export const PLUGIN_REPO = "jwilger/opencode-plugin-team-agreements";
|
|
605
|
+
// Using array join to avoid esbuild/tsc issues with ## at start of lines in template literals
|
|
606
|
+
export const COMMAND_TEMPLATE = [
|
|
607
|
+
"You are helping establish or review team agreements for this project. Team agreements define how humans and LLM agents collaborate on the codebase.",
|
|
608
|
+
"",
|
|
609
|
+
"User's request: $ARGUMENTS",
|
|
610
|
+
"",
|
|
611
|
+
"## Overview",
|
|
612
|
+
"",
|
|
613
|
+
"Team agreements are stored in TWO locations:",
|
|
614
|
+
"",
|
|
615
|
+
"1. **`docs/TEAM_AGREEMENTS.md`** - Comprehensive documentation of all team agreements (reference material for humans)",
|
|
616
|
+
"2. **`AGENTS.md`** - Only the rules that LLMs need to always have in context when working on the codebase",
|
|
617
|
+
"",
|
|
618
|
+
"The split ensures:",
|
|
619
|
+
"- Human team members have complete reference documentation",
|
|
620
|
+
"- LLM context isn't bloated with procedures they don't need constantly (deployment procedures, post-mortem processes, meeting cadences, etc.)",
|
|
621
|
+
"- LLMs can still reference the full docs when needed",
|
|
622
|
+
"",
|
|
623
|
+
"This is a comprehensive interview covering 7 categories of software development practices. The full interview typically takes 25-40 minutes, but you can skip topics or pause and resume later.",
|
|
624
|
+
"",
|
|
625
|
+
"## Step 1: Analyze Project & Existing Files",
|
|
626
|
+
"",
|
|
627
|
+
"**First, use the `analyze_project` tool** to detect:",
|
|
628
|
+
"- Languages, frameworks, and tools in use",
|
|
629
|
+
"- CI/CD, testing, and monitoring setup",
|
|
630
|
+
"- AI tools already configured",
|
|
631
|
+
"- Project characteristics (monorepo, library, frontend/backend)",
|
|
632
|
+
"",
|
|
633
|
+
"Then check for existing files:",
|
|
634
|
+
"1. **Read `docs/TEAM_AGREEMENTS.md`** (if it exists) - Full team agreements",
|
|
635
|
+
"2. **Read `AGENTS.md`** (if it exists) - LLM-specific rules",
|
|
636
|
+
"3. **Read `CLAUDE.md`** (if it exists) - Claude Code specific rules",
|
|
637
|
+
"",
|
|
638
|
+
"## Step 2: Determine the Scenario",
|
|
639
|
+
"",
|
|
640
|
+
"Based on what you find, you'll be in one of these scenarios:",
|
|
641
|
+
"",
|
|
642
|
+
"### Scenario A: No existing team agreements",
|
|
643
|
+
"- You'll create `docs/TEAM_AGREEMENTS.md` with full documentation",
|
|
644
|
+
"- You'll create/update `AGENTS.md` with LLM-relevant rules only",
|
|
645
|
+
"",
|
|
646
|
+
"### Scenario B: docs/TEAM_AGREEMENTS.md exists",
|
|
647
|
+
"- Present the user with options:",
|
|
648
|
+
" - **Review**: Display current content",
|
|
649
|
+
" - **Amend**: Modify or add to specific sections",
|
|
650
|
+
" - **Regenerate AGENTS.md**: Re-extract LLM-relevant rules",
|
|
651
|
+
"",
|
|
652
|
+
"### Scenario C: Only AGENTS.md/CLAUDE.md exist (legacy setup)",
|
|
653
|
+
"- Explain the new split approach",
|
|
654
|
+
"- Offer to migrate: create `docs/TEAM_AGREEMENTS.md` as comprehensive docs",
|
|
655
|
+
"- Update AGENTS.md to contain only LLM-relevant rules",
|
|
656
|
+
"- If CLAUDE.md exists, ensure it imports AGENTS.md and keep Claude-specific rules",
|
|
657
|
+
"",
|
|
658
|
+
"## Step 3: Present Categories Based on Analysis",
|
|
659
|
+
"",
|
|
660
|
+
"Show the project analysis results and present the 7 categories:",
|
|
661
|
+
"",
|
|
662
|
+
"```",
|
|
663
|
+
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
|
|
664
|
+
"TEAM AGREEMENTS INTERVIEW",
|
|
665
|
+
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
|
|
666
|
+
"",
|
|
667
|
+
"Based on your project analysis, here are the categories we'll cover:",
|
|
668
|
+
"",
|
|
669
|
+
"1. Code & Quality (4 topics) - How code is written",
|
|
670
|
+
"2. Integration & Delivery (4 topics) - How code flows",
|
|
671
|
+
"3. Operations & QA (4 topics) - How code runs",
|
|
672
|
+
"4. Documentation & Knowledge (3 topics) - How knowledge is captured",
|
|
673
|
+
"5. AI/LLM Collaboration (6 topics) - How humans and AI work together",
|
|
674
|
+
"6. Team Process (3 topics) - How the team works",
|
|
675
|
+
"7. Governance (2 topics) - How agreements evolve",
|
|
676
|
+
"",
|
|
677
|
+
"Estimated time: 25-40 minutes (you can pause anytime)",
|
|
678
|
+
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
|
|
679
|
+
"```",
|
|
680
|
+
"",
|
|
681
|
+
"Based on the project analysis, highlight which categories/topics are particularly relevant or potentially skippable. Ask if they want to proceed with all categories or skip any.",
|
|
682
|
+
"",
|
|
683
|
+
"## Step 4: Gather Team Agreements",
|
|
684
|
+
"",
|
|
685
|
+
"Guide through each category ONE question at a time. Show progress at the start of each category:",
|
|
686
|
+
"",
|
|
687
|
+
"```",
|
|
688
|
+
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
|
|
689
|
+
"Category X of 7: [Category Name]",
|
|
690
|
+
"Topics: [list topics in this category]",
|
|
691
|
+
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
|
|
692
|
+
"```",
|
|
693
|
+
"",
|
|
694
|
+
"### Interview Style Guidelines",
|
|
695
|
+
"",
|
|
696
|
+
"- Ask **ONE question at a time** - don't overwhelm",
|
|
697
|
+
"- **Discuss trade-offs** when relevant (e.g., strictness vs flexibility)",
|
|
698
|
+
"- **Use WebFetch** to research best practices when helpful",
|
|
699
|
+
"- Confirm understanding before moving on",
|
|
700
|
+
"- Capture the **why** behind decisions, not just the what",
|
|
701
|
+
"- Allow skipping topics with \"skip\" or \"not applicable\"",
|
|
702
|
+
"- Allow pausing with \"let's continue later\" (summarize progress)",
|
|
703
|
+
"",
|
|
704
|
+
"---",
|
|
705
|
+
"",
|
|
706
|
+
"## CATEGORY 1: CODE & QUALITY",
|
|
707
|
+
"",
|
|
708
|
+
"### 1.1 Programming Languages & Tech Stack",
|
|
709
|
+
"",
|
|
710
|
+
"Questions to explore:",
|
|
711
|
+
"- What programming language(s) will be used in this project?",
|
|
712
|
+
"- What is each language used for? (e.g., TypeScript for frontend, Rust for backend)",
|
|
713
|
+
"- Are there specific version requirements?",
|
|
714
|
+
"- Are there language-specific style guides to follow? (e.g., PEP 8, StandardJS, Google Style)",
|
|
715
|
+
"- What build tools and package managers are used?",
|
|
716
|
+
"- (If monorepo) How are packages/modules organized?",
|
|
717
|
+
"",
|
|
718
|
+
"### 1.2 Code Quality Standards",
|
|
719
|
+
"",
|
|
720
|
+
"Questions to explore:",
|
|
721
|
+
'- What does "great code" look like for this team?',
|
|
722
|
+
"- How should we prioritize: readability vs. performance vs. simplicity?",
|
|
723
|
+
"- Are there required design patterns? (SOLID, Clean Architecture, DDD, etc.)",
|
|
724
|
+
"- Are there anti-patterns to explicitly avoid?",
|
|
725
|
+
"- Error handling conventions? (throw vs return, error types, logging)",
|
|
726
|
+
"- Naming conventions for files, functions, variables, types, constants?",
|
|
727
|
+
"- Complexity limits? (max function length, cyclomatic complexity)",
|
|
728
|
+
"- Code comments philosophy? (when to comment, what to avoid)",
|
|
729
|
+
"",
|
|
730
|
+
"### 1.3 Code Review Process",
|
|
731
|
+
"",
|
|
732
|
+
"Questions to explore:",
|
|
733
|
+
"- What should code reviewers focus on? (correctness, style, security, performance)",
|
|
734
|
+
"- What's the expected turnaround time for reviews?",
|
|
735
|
+
"- Minimum number of approvers required?",
|
|
736
|
+
"- How should disagreements in review be handled?",
|
|
737
|
+
"- Are there PR size guidelines? (max lines, max files)",
|
|
738
|
+
"- Should authors self-review before requesting review?",
|
|
739
|
+
"- Review comment etiquette? (suggestions vs demands, tone)",
|
|
740
|
+
"- Are there things reviewers should NOT focus on? (let linters handle)",
|
|
741
|
+
"",
|
|
742
|
+
"### 1.4 Testing Requirements",
|
|
743
|
+
"",
|
|
744
|
+
"Questions to explore:",
|
|
745
|
+
'- What testing is required before code is considered "done"?',
|
|
746
|
+
"- Are there coverage thresholds? (line, branch, function)",
|
|
747
|
+
"- What types of tests are required? (unit, integration, e2e, smoke, contract)",
|
|
748
|
+
"- When should tests be written? (TDD, before PR, alongside implementation)",
|
|
749
|
+
"- Test naming conventions?",
|
|
750
|
+
"- Test data management? (factories, fixtures, mocking strategies)",
|
|
751
|
+
"- What makes a good test? (isolation, determinism, clarity)",
|
|
752
|
+
"- Security testing requirements?",
|
|
753
|
+
"- Performance testing requirements?",
|
|
754
|
+
"- Accessibility testing requirements? (if frontend)",
|
|
755
|
+
"",
|
|
756
|
+
"---",
|
|
757
|
+
"",
|
|
758
|
+
"## CATEGORY 2: INTEGRATION & DELIVERY",
|
|
759
|
+
"",
|
|
760
|
+
"### 2.1 Version Control & Branching",
|
|
761
|
+
"",
|
|
762
|
+
"Questions to explore:",
|
|
763
|
+
"- What branching strategy? (trunk-based, GitHub Flow, GitFlow)",
|
|
764
|
+
"- Branch naming conventions?",
|
|
765
|
+
"- Commit message format? (Conventional Commits, custom, freeform)",
|
|
766
|
+
"- Required commit message elements? (ticket numbers, scope, type)",
|
|
767
|
+
"- Rules on length, tense, capitalization for commits?",
|
|
768
|
+
"- Should commits be atomic? (one logical change per commit)",
|
|
769
|
+
"- Squash, merge, or rebase for PRs?",
|
|
770
|
+
"- Protected branches configuration?",
|
|
771
|
+
"",
|
|
772
|
+
"### 2.2 Continuous Integration",
|
|
773
|
+
"",
|
|
774
|
+
"Questions to explore:",
|
|
775
|
+
"- What CI checks are required before merge?",
|
|
776
|
+
"- Build requirements?",
|
|
777
|
+
"- Lint and format checks?",
|
|
778
|
+
"- Test requirements in CI?",
|
|
779
|
+
"- Who (if anyone) can bypass CI?",
|
|
780
|
+
"- How should flaky tests be handled?",
|
|
781
|
+
"- CI timeout limits?",
|
|
782
|
+
"",
|
|
783
|
+
"### 2.3 Deployment & Release",
|
|
784
|
+
"",
|
|
785
|
+
"Questions to explore:",
|
|
786
|
+
"- Deployment strategy? (blue-green, canary, rolling, direct)",
|
|
787
|
+
"- Environment management? (dev, staging, prod)",
|
|
788
|
+
"- Feature flags usage?",
|
|
789
|
+
"- Rollback procedures?",
|
|
790
|
+
"- Release cadence? (continuous, weekly, on-demand)",
|
|
791
|
+
"- Versioning scheme? (semver, calver, other)",
|
|
792
|
+
"- Release notes requirements?",
|
|
793
|
+
"- Hotfix procedures?",
|
|
794
|
+
"- Who can deploy? Who approves production deploys?",
|
|
795
|
+
"- Deploy freeze periods?",
|
|
796
|
+
"",
|
|
797
|
+
"### 2.4 Database & Schema Changes",
|
|
798
|
+
"",
|
|
799
|
+
"(Skip if no database detected)",
|
|
800
|
+
"",
|
|
801
|
+
"Questions to explore:",
|
|
802
|
+
"- Migration strategy?",
|
|
803
|
+
"- Schema change review process?",
|
|
804
|
+
"- Data migration testing requirements?",
|
|
805
|
+
"- Rollback procedures for migrations?",
|
|
806
|
+
"- Database naming conventions?",
|
|
807
|
+
"- Query performance standards?",
|
|
808
|
+
"",
|
|
809
|
+
"---",
|
|
810
|
+
"",
|
|
811
|
+
"## CATEGORY 3: OPERATIONS & QA",
|
|
812
|
+
"",
|
|
813
|
+
"### 3.1 Security Practices",
|
|
814
|
+
"",
|
|
815
|
+
"Questions to explore:",
|
|
816
|
+
"- Security review requirements for sensitive code?",
|
|
817
|
+
"- Secret management approach?",
|
|
818
|
+
"- Dependency vulnerability scanning?",
|
|
819
|
+
"- Authentication/authorization patterns to follow?",
|
|
820
|
+
"- Input validation requirements?",
|
|
821
|
+
"- Security incident response process?",
|
|
822
|
+
"- OWASP guidelines awareness?",
|
|
823
|
+
"",
|
|
824
|
+
"When discussing security, consider using WebFetch to look up current OWASP top 10 or relevant security guidelines.",
|
|
825
|
+
"",
|
|
826
|
+
"### 3.2 Monitoring & Observability",
|
|
827
|
+
"",
|
|
828
|
+
"Questions to explore:",
|
|
829
|
+
"- Logging standards? (what to log, format, levels)",
|
|
830
|
+
"- Metrics and alerting requirements?",
|
|
831
|
+
"- On-call procedures?",
|
|
832
|
+
"- Incident severity levels?",
|
|
833
|
+
"- Post-mortem/retrospective process?",
|
|
834
|
+
"- Runbook requirements?",
|
|
835
|
+
"- Error tracking approach?",
|
|
836
|
+
"",
|
|
837
|
+
"### 3.3 Performance Standards",
|
|
838
|
+
"",
|
|
839
|
+
"(Highlight for frontend-heavy projects)",
|
|
840
|
+
"",
|
|
841
|
+
"Questions to explore:",
|
|
842
|
+
"- Performance budgets? (load time, bundle size, memory)",
|
|
843
|
+
"- Load testing requirements?",
|
|
844
|
+
"- Profiling practices?",
|
|
845
|
+
"- Caching strategies?",
|
|
846
|
+
"- Performance regression testing?",
|
|
847
|
+
"",
|
|
848
|
+
"### 3.4 Accessibility & Internationalization",
|
|
849
|
+
"",
|
|
850
|
+
"(Primarily for user-facing applications)",
|
|
851
|
+
"",
|
|
852
|
+
"Questions to explore:",
|
|
853
|
+
"- WCAG compliance level? (A, AA, AAA)",
|
|
854
|
+
"- Accessibility testing requirements?",
|
|
855
|
+
"- Screen reader testing?",
|
|
856
|
+
"- Keyboard navigation requirements?",
|
|
857
|
+
"- Language/locale support?",
|
|
858
|
+
"- RTL support considerations?",
|
|
859
|
+
"- Translation workflow?",
|
|
860
|
+
"",
|
|
861
|
+
"---",
|
|
862
|
+
"",
|
|
863
|
+
"## CATEGORY 4: DOCUMENTATION & KNOWLEDGE",
|
|
864
|
+
"",
|
|
865
|
+
"### 4.1 Documentation Standards",
|
|
866
|
+
"",
|
|
867
|
+
"Questions to explore:",
|
|
868
|
+
"- README standards? (what must be included)",
|
|
869
|
+
"- API documentation requirements?",
|
|
870
|
+
"- Architecture documentation?",
|
|
871
|
+
"- In-code documentation? (JSDoc, docstrings, when to use)",
|
|
872
|
+
"- Changelog maintenance?",
|
|
873
|
+
"- Runbook/playbook requirements?",
|
|
874
|
+
"- Where does documentation live?",
|
|
875
|
+
"- Documentation review process?",
|
|
876
|
+
"",
|
|
877
|
+
"### 4.2 Architecture Decision Records (ADRs)",
|
|
878
|
+
"",
|
|
879
|
+
"Questions to explore:",
|
|
880
|
+
"- When should an ADR be written?",
|
|
881
|
+
"- ADR format/template?",
|
|
882
|
+
"- Review process for ADRs?",
|
|
883
|
+
"- Where are ADRs stored?",
|
|
884
|
+
"- How are superseded ADRs handled?",
|
|
885
|
+
"",
|
|
886
|
+
"### 4.3 Dependency Management",
|
|
887
|
+
"",
|
|
888
|
+
"Questions to explore:",
|
|
889
|
+
"- Dependency approval process?",
|
|
890
|
+
"- Version pinning strategy? (exact, range, floating)",
|
|
891
|
+
"- Upgrade cadence?",
|
|
892
|
+
"- License compliance requirements?",
|
|
893
|
+
"- Security vulnerability response time?",
|
|
894
|
+
"- Internal vs external package policies?",
|
|
895
|
+
"- Dependency audit process?",
|
|
896
|
+
"",
|
|
897
|
+
"---",
|
|
898
|
+
"",
|
|
899
|
+
"## CATEGORY 5: AI/LLM COLLABORATION",
|
|
900
|
+
"",
|
|
901
|
+
"This category is particularly important for teams using AI coding assistants like OpenCode, Claude Code, GitHub Copilot, Cursor, etc.",
|
|
902
|
+
"",
|
|
903
|
+
"### 5.1 AI Tools & Policies",
|
|
904
|
+
"",
|
|
905
|
+
"Questions to explore:",
|
|
906
|
+
"- What AI coding tools does the team use or approve?",
|
|
907
|
+
"- Is there an official stance on AI usage? (encouraged, allowed with guidelines, restricted)",
|
|
908
|
+
"- Are there tasks where AI should NOT be used? (security-sensitive, licensed code, etc.)",
|
|
909
|
+
"- How should AI tool usage be communicated or tracked?",
|
|
910
|
+
"- Data privacy considerations with AI tools?",
|
|
911
|
+
"",
|
|
912
|
+
"### 5.2 Autonomy Boundaries",
|
|
913
|
+
"",
|
|
914
|
+
"Questions to explore:",
|
|
915
|
+
"- What can AI agents do autonomously without asking?",
|
|
916
|
+
" - Create/modify files?",
|
|
917
|
+
" - Run tests?",
|
|
918
|
+
" - Make commits?",
|
|
919
|
+
" - Create pull requests?",
|
|
920
|
+
" - Merge code?",
|
|
921
|
+
" - Deploy?",
|
|
922
|
+
" - Install dependencies?",
|
|
923
|
+
"- What always requires human confirmation?",
|
|
924
|
+
"- Are there files/directories AI should never touch?",
|
|
925
|
+
"- Can AI access external resources? (web, APIs, MCP servers)",
|
|
926
|
+
"- Git operations permissions? (push, force push, branch deletion)",
|
|
927
|
+
"",
|
|
928
|
+
"### 5.3 AI Code Generation Standards",
|
|
929
|
+
"",
|
|
930
|
+
"Questions to explore:",
|
|
931
|
+
"- What quality standards apply to AI-generated code? (same as human, stricter, different)",
|
|
932
|
+
"- Should AI-generated code be clearly marked or attributed?",
|
|
933
|
+
"- Are there specific review requirements for AI-generated code?",
|
|
934
|
+
"- How should AI-generated tests be validated?",
|
|
935
|
+
"- Policy on accepting AI suggestions without modification?",
|
|
936
|
+
"- AI code that requires extra scrutiny? (security, data handling)",
|
|
937
|
+
"",
|
|
938
|
+
"### 5.4 Context & Session Management",
|
|
939
|
+
"",
|
|
940
|
+
"Questions to explore:",
|
|
941
|
+
"- How should context be structured in AGENTS.md?",
|
|
942
|
+
"- What information should AI always have access to?",
|
|
943
|
+
"- Session handoff summary requirements? (what to include when ending a session)",
|
|
944
|
+
"- How should AI communicate progress during long tasks?",
|
|
945
|
+
"- Information to preserve between sessions?",
|
|
946
|
+
"- How to handle context limits and compaction?",
|
|
947
|
+
"- Project-specific knowledge AI should know?",
|
|
948
|
+
"",
|
|
949
|
+
"### 5.5 Human Oversight & Escalation",
|
|
950
|
+
"",
|
|
951
|
+
"Questions to explore:",
|
|
952
|
+
"- What triggers should cause AI to stop and ask for human input?",
|
|
953
|
+
"- Self-verification requirements? (run tests, check lints before committing)",
|
|
954
|
+
"- How should AI handle suspected errors in its own output?",
|
|
955
|
+
"- Handling uncertainty or ambiguous requirements?",
|
|
956
|
+
"- Escalation path when AI gets stuck?",
|
|
957
|
+
"- Maximum scope of changes AI can make without checking in?",
|
|
958
|
+
"- How should AI flag concerns or potential issues?",
|
|
959
|
+
"",
|
|
960
|
+
"### 5.6 Learning & Improvement",
|
|
961
|
+
"",
|
|
962
|
+
"Questions to explore:",
|
|
963
|
+
"- How should effective AI prompts/patterns be shared with the team?",
|
|
964
|
+
"- Is there a team prompt library or best practices doc?",
|
|
965
|
+
"- How should AI mistakes be documented for learning?",
|
|
966
|
+
"- Retrospectives on AI effectiveness?",
|
|
967
|
+
"- How to update AGENTS.md as AI capabilities evolve?",
|
|
968
|
+
"- Feedback loop for improving AI collaboration?",
|
|
969
|
+
"",
|
|
970
|
+
"---",
|
|
971
|
+
"",
|
|
972
|
+
"## CATEGORY 6: TEAM PROCESS",
|
|
973
|
+
"",
|
|
974
|
+
"### 6.1 Development Methodology",
|
|
975
|
+
"",
|
|
976
|
+
"Questions to explore:",
|
|
977
|
+
"- What agile/methodology practices are used? (Scrum, Kanban, XP, none)",
|
|
978
|
+
"- Sprint/iteration cadence?",
|
|
979
|
+
"- Definition of Done?",
|
|
980
|
+
"- Definition of Ready?",
|
|
981
|
+
"- Estimation approach? (story points, t-shirt sizes, no estimates)",
|
|
982
|
+
"- Meeting cadence? (standups, planning, retros)",
|
|
983
|
+
"- WIP (work-in-progress) limits?",
|
|
984
|
+
"",
|
|
985
|
+
"### 6.2 Planning & Work Breakdown",
|
|
986
|
+
"",
|
|
987
|
+
"Questions to explore:",
|
|
988
|
+
"- How is work broken down? (epics → stories → tasks)",
|
|
989
|
+
"- Story/task sizing guidelines?",
|
|
990
|
+
"- Spike guidelines? (when, how long, expected output)",
|
|
991
|
+
"- Technical debt handling? (tracking, prioritization)",
|
|
992
|
+
"- Prioritization framework?",
|
|
993
|
+
"- How are blockers escalated?",
|
|
994
|
+
"",
|
|
995
|
+
"### 6.3 Communication & Collaboration",
|
|
996
|
+
"",
|
|
997
|
+
"Questions to explore:",
|
|
998
|
+
"- Async vs sync communication preferences?",
|
|
999
|
+
"- Response time expectations?",
|
|
1000
|
+
"- Decision documentation requirements?",
|
|
1001
|
+
"- Knowledge sharing practices?",
|
|
1002
|
+
"- Onboarding process for new team members?",
|
|
1003
|
+
"- Pair programming / mob programming practices?",
|
|
1004
|
+
"",
|
|
1005
|
+
"---",
|
|
1006
|
+
"",
|
|
1007
|
+
"## CATEGORY 7: GOVERNANCE",
|
|
1008
|
+
"",
|
|
1009
|
+
"### 7.1 Amendment Process",
|
|
1010
|
+
"",
|
|
1011
|
+
"Questions to explore:",
|
|
1012
|
+
"- How can these agreements be changed?",
|
|
1013
|
+
"- Who has authority to propose changes?",
|
|
1014
|
+
"- What's the review/approval process for amendments?",
|
|
1015
|
+
"- How should changes be communicated to the team?",
|
|
1016
|
+
"- How often should agreements be reviewed?",
|
|
1017
|
+
"",
|
|
1018
|
+
"### 7.2 Open-Ended",
|
|
1019
|
+
"",
|
|
1020
|
+
'After completing all categories, ask:',
|
|
1021
|
+
"",
|
|
1022
|
+
'"Is there anything else you\'d like to include in your team agreements that we haven\'t covered?"',
|
|
1023
|
+
"",
|
|
1024
|
+
"Allow free-form additions. If the user suggests a topic that seems generally useful, offer to file a GitHub issue to suggest it for the plugin:",
|
|
1025
|
+
"",
|
|
1026
|
+
'"Would you like me to suggest this topic to be included in future versions of the team-agreements plugin? I can file a GitHub issue for you."',
|
|
1027
|
+
"",
|
|
1028
|
+
"If yes, use the `suggest_team_agreement_topic` tool.",
|
|
1029
|
+
"",
|
|
1030
|
+
"---",
|
|
1031
|
+
"",
|
|
1032
|
+
"## Step 5: Generate Documents",
|
|
1033
|
+
"",
|
|
1034
|
+
"After gathering all agreements, generate both documents:",
|
|
1035
|
+
"",
|
|
1036
|
+
"### 5a. Create/Update docs/TEAM_AGREEMENTS.md",
|
|
1037
|
+
"",
|
|
1038
|
+
"- Create the `docs/` directory if it doesn't exist",
|
|
1039
|
+
"- Write the comprehensive documentation with ALL agreements",
|
|
1040
|
+
"- Preserve existing content if updating (intelligent merging)",
|
|
1041
|
+
"- Match existing document tone and style",
|
|
1042
|
+
"",
|
|
1043
|
+
"### 5b. Extract LLM-Relevant Rules to AGENTS.md",
|
|
1044
|
+
"",
|
|
1045
|
+
"- Extract only rules that affect day-to-day coding",
|
|
1046
|
+
"- Keep it concise - LLMs don't need verbose explanations",
|
|
1047
|
+
"- If AGENTS.md has other content (project description, architecture), preserve it",
|
|
1048
|
+
"- Add a reference to docs/TEAM_AGREEMENTS.md for complete details",
|
|
1049
|
+
"",
|
|
1050
|
+
"### Merging Guidelines",
|
|
1051
|
+
"",
|
|
1052
|
+
"When updating existing files:",
|
|
1053
|
+
"- **Preserve existing structure** - Don't reorganize content the user already has",
|
|
1054
|
+
"- **Avoid duplication** - If similar content exists, enhance rather than duplicate",
|
|
1055
|
+
"- **Maintain voice** - Match the existing document's tone and style",
|
|
1056
|
+
"",
|
|
1057
|
+
"## Step 6: Handle CLAUDE.md Coordination",
|
|
1058
|
+
"",
|
|
1059
|
+
"If CLAUDE.md exists or Claude-specific rules are needed:",
|
|
1060
|
+
"",
|
|
1061
|
+
"1. Ensure it has `@AGENTS.md` at the top to import the shared rules",
|
|
1062
|
+
"2. Keep only Claude-specific content in CLAUDE.md, such as:",
|
|
1063
|
+
" - Claude-specific behavior instructions",
|
|
1064
|
+
" - Claude-specific tool usage preferences",
|
|
1065
|
+
" - Anything that shouldn't apply to other agents",
|
|
1066
|
+
"",
|
|
1067
|
+
"## Step 7: Offer Enforcement Setup",
|
|
1068
|
+
"",
|
|
1069
|
+
"After generating/updating the agreements, use the `detect_enforcement_mechanisms` tool to check what enforcement is already in place, then offer to set up automatic enforcement for agreements that can be enforced programmatically.",
|
|
1070
|
+
"",
|
|
1071
|
+
"### Enforcement Mechanisms",
|
|
1072
|
+
"",
|
|
1073
|
+
"| Agreement Type | Enforcement Options |",
|
|
1074
|
+
"|----------------|---------------------|",
|
|
1075
|
+
"| Commit messages | commitlint, husky/lefthook hooks |",
|
|
1076
|
+
"| Code formatting | Prettier, Biome, pre-commit hooks |",
|
|
1077
|
+
"| Linting | ESLint, Biome, language-specific linters |",
|
|
1078
|
+
"| Testing | CI workflows, pre-push hooks |",
|
|
1079
|
+
"| Coverage | CI workflows with coverage gates |",
|
|
1080
|
+
"| Security | Dependabot, Snyk, CodeQL, CI workflows |",
|
|
1081
|
+
"| PR requirements | GitHub branch protection, rulesets |",
|
|
1082
|
+
"| Documentation | CI checks for README, API docs |",
|
|
1083
|
+
"",
|
|
1084
|
+
"### When Offering Enforcement",
|
|
1085
|
+
"",
|
|
1086
|
+
"- Only offer enforcement for agreements that can actually be automated",
|
|
1087
|
+
"- Explain what the enforcement will do and how it works",
|
|
1088
|
+
"- Get explicit confirmation before making changes",
|
|
1089
|
+
"- If enforcement tooling already exists, offer to update/extend it rather than replace it",
|
|
1090
|
+
"",
|
|
1091
|
+
"## Output Format",
|
|
1092
|
+
"",
|
|
1093
|
+
"You will create/update TWO files:",
|
|
1094
|
+
"",
|
|
1095
|
+
"### 1. docs/TEAM_AGREEMENTS.md (Comprehensive Documentation)",
|
|
1096
|
+
"",
|
|
1097
|
+
"This is the full reference document for the team. Include ALL agreements from ALL categories.",
|
|
1098
|
+
"",
|
|
1099
|
+
"```markdown",
|
|
1100
|
+
"# Team Agreements",
|
|
1101
|
+
"",
|
|
1102
|
+
"This document defines how our team collaborates on this codebase.",
|
|
1103
|
+
"*Last updated: [date]*",
|
|
1104
|
+
"",
|
|
1105
|
+
"> **For LLM Agents:** The rules you need to follow are in `AGENTS.md`.",
|
|
1106
|
+
"> This document is comprehensive reference material. Consult it when you need",
|
|
1107
|
+
"> detailed information about deployment, processes, or other team practices.",
|
|
1108
|
+
"",
|
|
1109
|
+
"## Code & Quality",
|
|
1110
|
+
"",
|
|
1111
|
+
"### Languages & Tech Stack",
|
|
1112
|
+
"[Full details...]",
|
|
1113
|
+
"",
|
|
1114
|
+
"### Code Quality Standards",
|
|
1115
|
+
"[Full details...]",
|
|
1116
|
+
"",
|
|
1117
|
+
"### Code Review Process",
|
|
1118
|
+
"[Full details including turnaround times, disagreement resolution, etc.]",
|
|
1119
|
+
"",
|
|
1120
|
+
"### Testing Requirements",
|
|
1121
|
+
"[Full details...]",
|
|
1122
|
+
"",
|
|
1123
|
+
"## Integration & Delivery",
|
|
1124
|
+
"",
|
|
1125
|
+
"### Version Control & Branching",
|
|
1126
|
+
"[Full details...]",
|
|
1127
|
+
"",
|
|
1128
|
+
"### Continuous Integration",
|
|
1129
|
+
"[Full details...]",
|
|
1130
|
+
"",
|
|
1131
|
+
"### Deployment & Release",
|
|
1132
|
+
"[Full details including environments, rollback procedures, hotfixes, etc.]",
|
|
1133
|
+
"",
|
|
1134
|
+
"### Database & Schema Changes",
|
|
1135
|
+
"[Full details if applicable...]",
|
|
1136
|
+
"",
|
|
1137
|
+
"## Operations & QA",
|
|
1138
|
+
"",
|
|
1139
|
+
"### Security Practices",
|
|
1140
|
+
"[Full details...]",
|
|
1141
|
+
"",
|
|
1142
|
+
"### Monitoring & Observability",
|
|
1143
|
+
"[Full details including on-call, incident response, post-mortems, etc.]",
|
|
1144
|
+
"",
|
|
1145
|
+
"### Performance Standards",
|
|
1146
|
+
"[Full details if applicable...]",
|
|
1147
|
+
"",
|
|
1148
|
+
"### Accessibility & Internationalization",
|
|
1149
|
+
"[Full details if applicable...]",
|
|
1150
|
+
"",
|
|
1151
|
+
"## Documentation & Knowledge",
|
|
1152
|
+
"",
|
|
1153
|
+
"### Documentation Standards",
|
|
1154
|
+
"[Full details...]",
|
|
1155
|
+
"",
|
|
1156
|
+
"### Architecture Decision Records",
|
|
1157
|
+
"[Full details...]",
|
|
1158
|
+
"",
|
|
1159
|
+
"### Dependency Management",
|
|
1160
|
+
"[Full details...]",
|
|
1161
|
+
"",
|
|
1162
|
+
"## AI/LLM Collaboration",
|
|
1163
|
+
"",
|
|
1164
|
+
"### AI Tools & Policies",
|
|
1165
|
+
"[Full details...]",
|
|
1166
|
+
"",
|
|
1167
|
+
"### Autonomy Boundaries",
|
|
1168
|
+
"[Full details - what AI can/cannot do autonomously...]",
|
|
1169
|
+
"",
|
|
1170
|
+
"### AI Code Generation Standards",
|
|
1171
|
+
"[Full details...]",
|
|
1172
|
+
"",
|
|
1173
|
+
"### Context & Session Management",
|
|
1174
|
+
"[Full details...]",
|
|
1175
|
+
"",
|
|
1176
|
+
"### Human Oversight & Escalation",
|
|
1177
|
+
"[Full details...]",
|
|
1178
|
+
"",
|
|
1179
|
+
"### Learning & Improvement",
|
|
1180
|
+
"[Full details...]",
|
|
1181
|
+
"",
|
|
1182
|
+
"## Team Process",
|
|
1183
|
+
"",
|
|
1184
|
+
"### Development Methodology",
|
|
1185
|
+
"[Full details...]",
|
|
1186
|
+
"",
|
|
1187
|
+
"### Planning & Work Breakdown",
|
|
1188
|
+
"[Full details...]",
|
|
1189
|
+
"",
|
|
1190
|
+
"### Communication & Collaboration",
|
|
1191
|
+
"[Full details...]",
|
|
1192
|
+
"",
|
|
1193
|
+
"## Governance",
|
|
1194
|
+
"",
|
|
1195
|
+
"### Amendment Process",
|
|
1196
|
+
"[Full details...]",
|
|
1197
|
+
"```",
|
|
1198
|
+
"",
|
|
1199
|
+
"### 2. AGENTS.md (LLM Context Rules)",
|
|
1200
|
+
"",
|
|
1201
|
+
"This file contains ONLY the rules that LLMs need constantly in context. Extract rules that affect day-to-day coding work.",
|
|
1202
|
+
"",
|
|
1203
|
+
"**What to include in AGENTS.md:**",
|
|
1204
|
+
"- Code quality standards and patterns",
|
|
1205
|
+
"- Testing requirements",
|
|
1206
|
+
"- Commit message format",
|
|
1207
|
+
"- Code review expectations",
|
|
1208
|
+
"- Branching/PR conventions",
|
|
1209
|
+
"- AI autonomy boundaries",
|
|
1210
|
+
"- AI code generation standards",
|
|
1211
|
+
"- Escalation triggers",
|
|
1212
|
+
"",
|
|
1213
|
+
"**What NOT to include in AGENTS.md (keep only in docs/TEAM_AGREEMENTS.md):**",
|
|
1214
|
+
"- Deployment procedures (reference when needed)",
|
|
1215
|
+
"- On-call and incident procedures",
|
|
1216
|
+
"- Post-mortem processes",
|
|
1217
|
+
"- Meeting cadences and ceremonies",
|
|
1218
|
+
"- Detailed release procedures",
|
|
1219
|
+
"- Onboarding processes",
|
|
1220
|
+
"- Communication preferences",
|
|
1221
|
+
"",
|
|
1222
|
+
"```markdown",
|
|
1223
|
+
"# Agent Instructions",
|
|
1224
|
+
"",
|
|
1225
|
+
"This file contains rules for LLM agents working on this codebase.",
|
|
1226
|
+
"For complete team agreements, see `docs/TEAM_AGREEMENTS.md`.",
|
|
1227
|
+
"",
|
|
1228
|
+
"## Code Standards",
|
|
1229
|
+
"",
|
|
1230
|
+
"[Concise code quality rules...]",
|
|
1231
|
+
"[Naming conventions...]",
|
|
1232
|
+
"[Error handling patterns...]",
|
|
1233
|
+
"",
|
|
1234
|
+
"## Testing",
|
|
1235
|
+
"",
|
|
1236
|
+
"[What tests are required...]",
|
|
1237
|
+
"[Coverage requirements...]",
|
|
1238
|
+
"",
|
|
1239
|
+
"## Version Control",
|
|
1240
|
+
"",
|
|
1241
|
+
"[Commit message format...]",
|
|
1242
|
+
"[Branch naming...]",
|
|
1243
|
+
"[PR requirements...]",
|
|
1244
|
+
"",
|
|
1245
|
+
"## Code Review",
|
|
1246
|
+
"",
|
|
1247
|
+
"[What reviewers look for...]",
|
|
1248
|
+
"[Self-review checklist...]",
|
|
1249
|
+
"",
|
|
1250
|
+
"## AI Autonomy",
|
|
1251
|
+
"",
|
|
1252
|
+
"[What you can do without asking...]",
|
|
1253
|
+
"[What requires human approval...]",
|
|
1254
|
+
"[Files/areas to avoid...]",
|
|
1255
|
+
"",
|
|
1256
|
+
"## AI Code Standards",
|
|
1257
|
+
"",
|
|
1258
|
+
"[Quality requirements for AI-generated code...]",
|
|
1259
|
+
"[When to run tests...]",
|
|
1260
|
+
"[Verification requirements...]",
|
|
1261
|
+
"",
|
|
1262
|
+
"## Escalation",
|
|
1263
|
+
"",
|
|
1264
|
+
"[When to stop and ask...]",
|
|
1265
|
+
"[How to flag concerns...]",
|
|
1266
|
+
"```",
|
|
1267
|
+
"",
|
|
1268
|
+
"### 3. CLAUDE.md Coordination (if applicable)",
|
|
1269
|
+
"",
|
|
1270
|
+
"If CLAUDE.md exists or Claude-specific rules are needed:",
|
|
1271
|
+
"- Ensure it imports AGENTS.md: `@AGENTS.md` at the top",
|
|
1272
|
+
"- Keep only Claude-specific behavior/preferences in CLAUDE.md",
|
|
1273
|
+
"```",
|
|
1274
|
+
"",
|
|
1275
|
+
"## Important Guidelines",
|
|
1276
|
+
"",
|
|
1277
|
+
"- Be conversational and collaborative",
|
|
1278
|
+
"- **ONE question at a time** - don't overwhelm",
|
|
1279
|
+
"- Respect the user's expertise and preferences",
|
|
1280
|
+
"- If the user provides a specific request in their message, address that first",
|
|
1281
|
+
"- Allow skipping entire categories or individual topics",
|
|
1282
|
+
"- Support pausing and resuming the interview",
|
|
1283
|
+
"- Show progress indicators at category transitions",
|
|
1284
|
+
"- Use WebFetch to research best practices when relevant",
|
|
1285
|
+
'- Capture the "why" behind decisions, not just the "what"',
|
|
1286
|
+
"- When merging, preserve the user's existing content and style",
|
|
1287
|
+
"- Always explain what you're about to do before making file changes",
|
|
1288
|
+
"- The AI/LLM Collaboration category is particularly important - don't rush through it",
|
|
1289
|
+
].join("\n");
|
|
1290
|
+
/**
|
|
1291
|
+
* Check if a file exists at the given path.
|
|
1292
|
+
*/
|
|
1293
|
+
export async function fileExists(path) {
|
|
1294
|
+
try {
|
|
1295
|
+
await access(path);
|
|
1296
|
+
return true;
|
|
1297
|
+
}
|
|
1298
|
+
catch {
|
|
1299
|
+
return false;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Check if the gh CLI is installed and authenticated.
|
|
1304
|
+
*/
|
|
1305
|
+
export async function isGhAvailable() {
|
|
1306
|
+
try {
|
|
1307
|
+
await execAsync("gh auth status");
|
|
1308
|
+
return true;
|
|
1309
|
+
}
|
|
1310
|
+
catch {
|
|
1311
|
+
return false;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Load team agreements from the project directory.
|
|
1316
|
+
* Checks AGENTS.md first (primary), then CLAUDE.md (fallback).
|
|
1317
|
+
* Returns the content if found, null otherwise.
|
|
1318
|
+
*/
|
|
1319
|
+
export async function loadTeamAgreements(directory) {
|
|
1320
|
+
// Check AGENTS.md first (OpenCode native)
|
|
1321
|
+
const agentsPath = join(directory, "AGENTS.md");
|
|
1322
|
+
// Check CLAUDE.md as fallback (Claude Code native)
|
|
1323
|
+
const claudePath = join(directory, "CLAUDE.md");
|
|
1324
|
+
try {
|
|
1325
|
+
const content = await readFile(agentsPath, "utf-8");
|
|
1326
|
+
return ("## Team Agreements (from AGENTS.md)\n\nThe following team agreements are in effect for this project:\n\n" +
|
|
1327
|
+
content);
|
|
1328
|
+
}
|
|
1329
|
+
catch {
|
|
1330
|
+
// Try CLAUDE.md as fallback
|
|
1331
|
+
try {
|
|
1332
|
+
const content = await readFile(claudePath, "utf-8");
|
|
1333
|
+
return ("## Team Agreements (from CLAUDE.md)\n\nThe following team agreements are in effect for this project:\n\n" +
|
|
1334
|
+
content);
|
|
1335
|
+
}
|
|
1336
|
+
catch {
|
|
1337
|
+
return null;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Format suggested questions as a markdown list.
|
|
1343
|
+
*/
|
|
1344
|
+
export function formatQuestionsAsMarkdown(questions) {
|
|
1345
|
+
return questions.map((q) => "- " + q).join("\n");
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Build the issue body for a topic suggestion.
|
|
1349
|
+
*/
|
|
1350
|
+
export function buildTopicIssueBody(args) {
|
|
1351
|
+
const questionsFormatted = formatQuestionsAsMarkdown(args.suggested_questions);
|
|
1352
|
+
return [
|
|
1353
|
+
"## Topic Name",
|
|
1354
|
+
args.topic_name,
|
|
1355
|
+
"",
|
|
1356
|
+
"## Description",
|
|
1357
|
+
args.description,
|
|
1358
|
+
"",
|
|
1359
|
+
"## Suggested Questions",
|
|
1360
|
+
questionsFormatted,
|
|
1361
|
+
"",
|
|
1362
|
+
"## Example Agreement",
|
|
1363
|
+
args.example_agreement || "_No example provided_",
|
|
1364
|
+
"",
|
|
1365
|
+
"## Additional Context",
|
|
1366
|
+
"_This issue was automatically created via the team-agreements plugin._",
|
|
1367
|
+
].join("\n");
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Detect existing enforcement mechanisms in the project.
|
|
1371
|
+
*/
|
|
1372
|
+
export async function detectEnforcementMechanisms(directory) {
|
|
1373
|
+
const mechanisms = [];
|
|
1374
|
+
// Pre-commit hooks
|
|
1375
|
+
const huskyPath = join(directory, ".husky");
|
|
1376
|
+
const lefthookPath = join(directory, "lefthook.yml");
|
|
1377
|
+
const lefthookAltPath = join(directory, ".lefthook.yml");
|
|
1378
|
+
const preCommitPath = join(directory, ".pre-commit-config.yaml");
|
|
1379
|
+
if (await fileExists(huskyPath)) {
|
|
1380
|
+
mechanisms.push({
|
|
1381
|
+
type: "pre-commit",
|
|
1382
|
+
name: "husky",
|
|
1383
|
+
detected: true,
|
|
1384
|
+
configFile: ".husky/",
|
|
1385
|
+
notes: "Git hooks manager for Node.js projects",
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
if (await fileExists(lefthookPath)) {
|
|
1389
|
+
mechanisms.push({
|
|
1390
|
+
type: "pre-commit",
|
|
1391
|
+
name: "lefthook",
|
|
1392
|
+
detected: true,
|
|
1393
|
+
configFile: "lefthook.yml",
|
|
1394
|
+
notes: "Fast, cross-platform git hooks manager",
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
else if (await fileExists(lefthookAltPath)) {
|
|
1398
|
+
mechanisms.push({
|
|
1399
|
+
type: "pre-commit",
|
|
1400
|
+
name: "lefthook",
|
|
1401
|
+
detected: true,
|
|
1402
|
+
configFile: ".lefthook.yml",
|
|
1403
|
+
notes: "Fast, cross-platform git hooks manager",
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
if (await fileExists(preCommitPath)) {
|
|
1407
|
+
mechanisms.push({
|
|
1408
|
+
type: "pre-commit",
|
|
1409
|
+
name: "pre-commit",
|
|
1410
|
+
detected: true,
|
|
1411
|
+
configFile: ".pre-commit-config.yaml",
|
|
1412
|
+
notes: "Python-based pre-commit framework",
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
// Commit message validation
|
|
1416
|
+
const commitlintPath = join(directory, "commitlint.config.js");
|
|
1417
|
+
const commitlintCjsPath = join(directory, "commitlint.config.cjs");
|
|
1418
|
+
const commitlintJsonPath = join(directory, ".commitlintrc.json");
|
|
1419
|
+
if ((await fileExists(commitlintPath)) ||
|
|
1420
|
+
(await fileExists(commitlintCjsPath)) ||
|
|
1421
|
+
(await fileExists(commitlintJsonPath))) {
|
|
1422
|
+
mechanisms.push({
|
|
1423
|
+
type: "commit-validation",
|
|
1424
|
+
name: "commitlint",
|
|
1425
|
+
detected: true,
|
|
1426
|
+
configFile: (await fileExists(commitlintPath))
|
|
1427
|
+
? "commitlint.config.js"
|
|
1428
|
+
: (await fileExists(commitlintCjsPath))
|
|
1429
|
+
? "commitlint.config.cjs"
|
|
1430
|
+
: ".commitlintrc.json",
|
|
1431
|
+
notes: "Lint commit messages against conventional commit format",
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
// Linters
|
|
1435
|
+
const eslintPath = join(directory, ".eslintrc.json");
|
|
1436
|
+
const eslintJsPath = join(directory, "eslint.config.js");
|
|
1437
|
+
const eslintMjsPath = join(directory, "eslint.config.mjs");
|
|
1438
|
+
const biomePath = join(directory, "biome.json");
|
|
1439
|
+
const biomeJsoncPath = join(directory, "biome.jsonc");
|
|
1440
|
+
if ((await fileExists(eslintPath)) ||
|
|
1441
|
+
(await fileExists(eslintJsPath)) ||
|
|
1442
|
+
(await fileExists(eslintMjsPath))) {
|
|
1443
|
+
mechanisms.push({
|
|
1444
|
+
type: "linter",
|
|
1445
|
+
name: "eslint",
|
|
1446
|
+
detected: true,
|
|
1447
|
+
configFile: (await fileExists(eslintPath))
|
|
1448
|
+
? ".eslintrc.json"
|
|
1449
|
+
: (await fileExists(eslintJsPath))
|
|
1450
|
+
? "eslint.config.js"
|
|
1451
|
+
: "eslint.config.mjs",
|
|
1452
|
+
notes: "JavaScript/TypeScript linter",
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
if ((await fileExists(biomePath)) || (await fileExists(biomeJsoncPath))) {
|
|
1456
|
+
mechanisms.push({
|
|
1457
|
+
type: "linter",
|
|
1458
|
+
name: "biome",
|
|
1459
|
+
detected: true,
|
|
1460
|
+
configFile: (await fileExists(biomePath)) ? "biome.json" : "biome.jsonc",
|
|
1461
|
+
notes: "Fast formatter and linter for JS/TS/JSON",
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
// CI Workflows
|
|
1465
|
+
const githubWorkflowsPath = join(directory, ".github", "workflows");
|
|
1466
|
+
if (await fileExists(githubWorkflowsPath)) {
|
|
1467
|
+
mechanisms.push({
|
|
1468
|
+
type: "ci",
|
|
1469
|
+
name: "github-actions",
|
|
1470
|
+
detected: true,
|
|
1471
|
+
configFile: ".github/workflows/",
|
|
1472
|
+
notes: "GitHub Actions CI/CD workflows",
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
const gitlabCiPath = join(directory, ".gitlab-ci.yml");
|
|
1476
|
+
if (await fileExists(gitlabCiPath)) {
|
|
1477
|
+
mechanisms.push({
|
|
1478
|
+
type: "ci",
|
|
1479
|
+
name: "gitlab-ci",
|
|
1480
|
+
detected: true,
|
|
1481
|
+
configFile: ".gitlab-ci.yml",
|
|
1482
|
+
notes: "GitLab CI/CD pipeline",
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
const circleCiPath = join(directory, ".circleci", "config.yml");
|
|
1486
|
+
if (await fileExists(circleCiPath)) {
|
|
1487
|
+
mechanisms.push({
|
|
1488
|
+
type: "ci",
|
|
1489
|
+
name: "circleci",
|
|
1490
|
+
detected: true,
|
|
1491
|
+
configFile: ".circleci/config.yml",
|
|
1492
|
+
notes: "CircleCI pipeline",
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
// PR Templates
|
|
1496
|
+
const prTemplatePath = join(directory, ".github", "pull_request_template.md");
|
|
1497
|
+
const prTemplateAltPath = join(directory, ".github", "PULL_REQUEST_TEMPLATE.md");
|
|
1498
|
+
if ((await fileExists(prTemplatePath)) ||
|
|
1499
|
+
(await fileExists(prTemplateAltPath))) {
|
|
1500
|
+
mechanisms.push({
|
|
1501
|
+
type: "pr-template",
|
|
1502
|
+
name: "github-pr-template",
|
|
1503
|
+
detected: true,
|
|
1504
|
+
configFile: (await fileExists(prTemplatePath))
|
|
1505
|
+
? ".github/pull_request_template.md"
|
|
1506
|
+
: ".github/PULL_REQUEST_TEMPLATE.md",
|
|
1507
|
+
notes: "GitHub Pull Request template",
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
// OpenCode configuration
|
|
1511
|
+
const opencodePath = join(directory, "opencode.json");
|
|
1512
|
+
if (await fileExists(opencodePath)) {
|
|
1513
|
+
mechanisms.push({
|
|
1514
|
+
type: "opencode",
|
|
1515
|
+
name: "opencode-config",
|
|
1516
|
+
detected: true,
|
|
1517
|
+
configFile: "opencode.json",
|
|
1518
|
+
notes: "OpenCode configuration with hooks and plugins",
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
// Formatters
|
|
1522
|
+
const prettierPath = join(directory, ".prettierrc");
|
|
1523
|
+
const prettierJsonPath = join(directory, ".prettierrc.json");
|
|
1524
|
+
const prettierJsPath = join(directory, "prettier.config.js");
|
|
1525
|
+
if ((await fileExists(prettierPath)) ||
|
|
1526
|
+
(await fileExists(prettierJsonPath)) ||
|
|
1527
|
+
(await fileExists(prettierJsPath))) {
|
|
1528
|
+
mechanisms.push({
|
|
1529
|
+
type: "formatter",
|
|
1530
|
+
name: "prettier",
|
|
1531
|
+
detected: true,
|
|
1532
|
+
configFile: (await fileExists(prettierPath))
|
|
1533
|
+
? ".prettierrc"
|
|
1534
|
+
: (await fileExists(prettierJsonPath))
|
|
1535
|
+
? ".prettierrc.json"
|
|
1536
|
+
: "prettier.config.js",
|
|
1537
|
+
notes: "Code formatter for JS/TS/CSS/HTML/JSON/MD",
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
return mechanisms;
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Format enforcement detection results as markdown.
|
|
1544
|
+
*/
|
|
1545
|
+
export function formatEnforcementResults(mechanisms) {
|
|
1546
|
+
if (mechanisms.length === 0) {
|
|
1547
|
+
return [
|
|
1548
|
+
"## Detected Enforcement Mechanisms",
|
|
1549
|
+
"",
|
|
1550
|
+
"No existing enforcement mechanisms detected. This is a great opportunity to set up automation for your team agreements!",
|
|
1551
|
+
"",
|
|
1552
|
+
"### Available Options",
|
|
1553
|
+
"",
|
|
1554
|
+
"You can set up any of the following:",
|
|
1555
|
+
"",
|
|
1556
|
+
"**Pre-commit Hooks**",
|
|
1557
|
+
"- husky (Node.js projects)",
|
|
1558
|
+
"- lefthook (cross-platform, fast)",
|
|
1559
|
+
"- pre-commit (Python-based, language-agnostic)",
|
|
1560
|
+
"",
|
|
1561
|
+
"**Commit Message Validation**",
|
|
1562
|
+
"- commitlint (conventional commits)",
|
|
1563
|
+
"",
|
|
1564
|
+
"**Linting/Formatting**",
|
|
1565
|
+
"- ESLint (JavaScript/TypeScript)",
|
|
1566
|
+
"- Biome (JS/TS/JSON, fast)",
|
|
1567
|
+
"- Prettier (formatting)",
|
|
1568
|
+
"",
|
|
1569
|
+
"**CI/CD**",
|
|
1570
|
+
"- GitHub Actions",
|
|
1571
|
+
"- GitLab CI",
|
|
1572
|
+
"- CircleCI",
|
|
1573
|
+
"",
|
|
1574
|
+
"**GitHub Features**",
|
|
1575
|
+
"- Pull Request templates",
|
|
1576
|
+
"- Branch protection rulesets",
|
|
1577
|
+
].join("\n");
|
|
1578
|
+
}
|
|
1579
|
+
const byType = {};
|
|
1580
|
+
for (const m of mechanisms) {
|
|
1581
|
+
if (!byType[m.type])
|
|
1582
|
+
byType[m.type] = [];
|
|
1583
|
+
byType[m.type].push(m);
|
|
1584
|
+
}
|
|
1585
|
+
const lines = [
|
|
1586
|
+
"## Detected Enforcement Mechanisms",
|
|
1587
|
+
"",
|
|
1588
|
+
"The following enforcement mechanisms are already in place:",
|
|
1589
|
+
"",
|
|
1590
|
+
];
|
|
1591
|
+
const typeLabels = {
|
|
1592
|
+
"pre-commit": "Pre-commit Hooks",
|
|
1593
|
+
"commit-validation": "Commit Message Validation",
|
|
1594
|
+
linter: "Linters",
|
|
1595
|
+
ci: "CI/CD Pipelines",
|
|
1596
|
+
"pr-template": "PR Templates",
|
|
1597
|
+
opencode: "OpenCode Configuration",
|
|
1598
|
+
formatter: "Formatters",
|
|
1599
|
+
};
|
|
1600
|
+
for (const [type, mechs] of Object.entries(byType)) {
|
|
1601
|
+
lines.push("### " + (typeLabels[type] || type));
|
|
1602
|
+
lines.push("");
|
|
1603
|
+
for (const m of mechs) {
|
|
1604
|
+
lines.push("- **" + m.name + "** (`" + m.configFile + "`): " + m.notes);
|
|
1605
|
+
}
|
|
1606
|
+
lines.push("");
|
|
1607
|
+
}
|
|
1608
|
+
lines.push("### Recommendations");
|
|
1609
|
+
lines.push("");
|
|
1610
|
+
lines.push("Based on what's already set up, consider extending or integrating with these existing tools rather than adding new ones.");
|
|
1611
|
+
return lines.join("\n");
|
|
1612
|
+
}
|
|
1613
|
+
//# sourceMappingURL=utils.js.map
|