@mainahq/core 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/package.json +37 -0
- package/src/ai/__tests__/ai.test.ts +207 -0
- package/src/ai/__tests__/design-approaches.test.ts +192 -0
- package/src/ai/__tests__/spec-questions.test.ts +191 -0
- package/src/ai/__tests__/tiers.test.ts +110 -0
- package/src/ai/commit-msg.ts +28 -0
- package/src/ai/design-approaches.ts +76 -0
- package/src/ai/index.ts +205 -0
- package/src/ai/pr-summary.ts +60 -0
- package/src/ai/spec-questions.ts +74 -0
- package/src/ai/tiers.ts +52 -0
- package/src/ai/try-generate.ts +89 -0
- package/src/ai/validate.ts +66 -0
- package/src/benchmark/__tests__/reporter.test.ts +525 -0
- package/src/benchmark/__tests__/runner.test.ts +113 -0
- package/src/benchmark/__tests__/story-loader.test.ts +152 -0
- package/src/benchmark/reporter.ts +332 -0
- package/src/benchmark/runner.ts +91 -0
- package/src/benchmark/story-loader.ts +88 -0
- package/src/benchmark/types.ts +95 -0
- package/src/cache/__tests__/keys.test.ts +97 -0
- package/src/cache/__tests__/manager.test.ts +312 -0
- package/src/cache/__tests__/ttl.test.ts +94 -0
- package/src/cache/keys.ts +44 -0
- package/src/cache/manager.ts +231 -0
- package/src/cache/ttl.ts +77 -0
- package/src/config/__tests__/config.test.ts +376 -0
- package/src/config/index.ts +198 -0
- package/src/context/__tests__/budget.test.ts +179 -0
- package/src/context/__tests__/engine.test.ts +163 -0
- package/src/context/__tests__/episodic.test.ts +291 -0
- package/src/context/__tests__/relevance.test.ts +323 -0
- package/src/context/__tests__/retrieval.test.ts +143 -0
- package/src/context/__tests__/selector.test.ts +174 -0
- package/src/context/__tests__/semantic.test.ts +252 -0
- package/src/context/__tests__/treesitter.test.ts +229 -0
- package/src/context/__tests__/working.test.ts +236 -0
- package/src/context/budget.ts +130 -0
- package/src/context/engine.ts +394 -0
- package/src/context/episodic.ts +251 -0
- package/src/context/relevance.ts +325 -0
- package/src/context/retrieval.ts +325 -0
- package/src/context/selector.ts +93 -0
- package/src/context/semantic.ts +331 -0
- package/src/context/treesitter.ts +216 -0
- package/src/context/working.ts +192 -0
- package/src/db/__tests__/db.test.ts +151 -0
- package/src/db/index.ts +211 -0
- package/src/db/schema.ts +84 -0
- package/src/design/__tests__/design.test.ts +310 -0
- package/src/design/__tests__/generate-hld-lld.test.ts +109 -0
- package/src/design/__tests__/review.test.ts +561 -0
- package/src/design/index.ts +297 -0
- package/src/design/review.ts +327 -0
- package/src/explain/__tests__/explain.test.ts +173 -0
- package/src/explain/index.ts +181 -0
- package/src/features/__tests__/analyzer.test.ts +358 -0
- package/src/features/__tests__/checklist.test.ts +454 -0
- package/src/features/__tests__/numbering.test.ts +319 -0
- package/src/features/__tests__/quality.test.ts +295 -0
- package/src/features/__tests__/traceability.test.ts +147 -0
- package/src/features/analyzer.ts +445 -0
- package/src/features/checklist.ts +366 -0
- package/src/features/index.ts +18 -0
- package/src/features/numbering.ts +404 -0
- package/src/features/quality.ts +349 -0
- package/src/features/test-stubs.ts +157 -0
- package/src/features/traceability.ts +260 -0
- package/src/feedback/__tests__/async-feedback.test.ts +52 -0
- package/src/feedback/__tests__/collector.test.ts +219 -0
- package/src/feedback/__tests__/compress.test.ts +150 -0
- package/src/feedback/__tests__/preferences.test.ts +169 -0
- package/src/feedback/collector.ts +135 -0
- package/src/feedback/compress.ts +92 -0
- package/src/feedback/preferences.ts +108 -0
- package/src/git/__tests__/git.test.ts +62 -0
- package/src/git/index.ts +110 -0
- package/src/hooks/__tests__/runner.test.ts +266 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/runner.ts +130 -0
- package/src/index.ts +356 -0
- package/src/init/__tests__/init.test.ts +228 -0
- package/src/init/index.ts +364 -0
- package/src/language/__tests__/detect.test.ts +77 -0
- package/src/language/__tests__/profile.test.ts +51 -0
- package/src/language/detect.ts +70 -0
- package/src/language/profile.ts +110 -0
- package/src/prompts/__tests__/defaults.test.ts +52 -0
- package/src/prompts/__tests__/engine.test.ts +183 -0
- package/src/prompts/__tests__/evolution-resolve.test.ts +169 -0
- package/src/prompts/__tests__/evolution.test.ts +187 -0
- package/src/prompts/__tests__/loader.test.ts +105 -0
- package/src/prompts/candidates/review-v2.md +55 -0
- package/src/prompts/defaults/ai-review.md +49 -0
- package/src/prompts/defaults/commit.md +30 -0
- package/src/prompts/defaults/context.md +26 -0
- package/src/prompts/defaults/design-approaches.md +57 -0
- package/src/prompts/defaults/design-hld-lld.md +55 -0
- package/src/prompts/defaults/design.md +53 -0
- package/src/prompts/defaults/explain.md +31 -0
- package/src/prompts/defaults/fix.md +32 -0
- package/src/prompts/defaults/index.ts +38 -0
- package/src/prompts/defaults/review.md +41 -0
- package/src/prompts/defaults/spec-questions.md +59 -0
- package/src/prompts/defaults/tests.md +72 -0
- package/src/prompts/engine.ts +137 -0
- package/src/prompts/evolution.ts +409 -0
- package/src/prompts/loader.ts +71 -0
- package/src/review/__tests__/review.test.ts +288 -0
- package/src/review/comprehensive.ts +362 -0
- package/src/review/index.ts +417 -0
- package/src/stats/__tests__/tracker.test.ts +323 -0
- package/src/stats/index.ts +11 -0
- package/src/stats/tracker.ts +492 -0
- package/src/ticket/__tests__/ticket.test.ts +273 -0
- package/src/ticket/index.ts +185 -0
- package/src/utils.ts +87 -0
- package/src/verify/__tests__/ai-review.test.ts +242 -0
- package/src/verify/__tests__/coverage.test.ts +83 -0
- package/src/verify/__tests__/detect.test.ts +175 -0
- package/src/verify/__tests__/diff-filter.test.ts +338 -0
- package/src/verify/__tests__/fix.test.ts +478 -0
- package/src/verify/__tests__/linters/clippy.test.ts +45 -0
- package/src/verify/__tests__/linters/go-vet.test.ts +27 -0
- package/src/verify/__tests__/linters/ruff.test.ts +64 -0
- package/src/verify/__tests__/mutation.test.ts +141 -0
- package/src/verify/__tests__/pipeline.test.ts +553 -0
- package/src/verify/__tests__/proof.test.ts +97 -0
- package/src/verify/__tests__/secretlint.test.ts +190 -0
- package/src/verify/__tests__/semgrep.test.ts +217 -0
- package/src/verify/__tests__/slop.test.ts +366 -0
- package/src/verify/__tests__/sonar.test.ts +113 -0
- package/src/verify/__tests__/syntax-guard.test.ts +227 -0
- package/src/verify/__tests__/trivy.test.ts +191 -0
- package/src/verify/__tests__/visual.test.ts +139 -0
- package/src/verify/ai-review.ts +276 -0
- package/src/verify/coverage.ts +134 -0
- package/src/verify/detect.ts +171 -0
- package/src/verify/diff-filter.ts +183 -0
- package/src/verify/fix.ts +317 -0
- package/src/verify/linters/clippy.ts +52 -0
- package/src/verify/linters/go-vet.ts +32 -0
- package/src/verify/linters/ruff.ts +47 -0
- package/src/verify/mutation.ts +143 -0
- package/src/verify/pipeline.ts +328 -0
- package/src/verify/proof.ts +277 -0
- package/src/verify/secretlint.ts +168 -0
- package/src/verify/semgrep.ts +170 -0
- package/src/verify/slop.ts +493 -0
- package/src/verify/sonar.ts +146 -0
- package/src/verify/syntax-guard.ts +251 -0
- package/src/verify/trivy.ts +161 -0
- package/src/verify/visual.ts +460 -0
- package/src/workflow/__tests__/context.test.ts +110 -0
- package/src/workflow/context.ts +81 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
export const VERSION = "0.1.0";
|
|
2
|
+
|
|
3
|
+
// AI
|
|
4
|
+
export { generateCommitMessage } from "./ai/commit-msg";
|
|
5
|
+
export {
|
|
6
|
+
type DesignApproach,
|
|
7
|
+
generateDesignApproaches,
|
|
8
|
+
} from "./ai/design-approaches";
|
|
9
|
+
export { generate } from "./ai/index";
|
|
10
|
+
export { generatePrSummary } from "./ai/pr-summary";
|
|
11
|
+
export {
|
|
12
|
+
generateSpecQuestions,
|
|
13
|
+
type SpecQuestion,
|
|
14
|
+
} from "./ai/spec-questions";
|
|
15
|
+
export {
|
|
16
|
+
type DelegationPrompt,
|
|
17
|
+
type TryAIResult,
|
|
18
|
+
tryAIGenerate,
|
|
19
|
+
} from "./ai/try-generate";
|
|
20
|
+
// AI validation
|
|
21
|
+
export { type AIValidationResult, validateAIOutput } from "./ai/validate";
|
|
22
|
+
export {
|
|
23
|
+
buildReport,
|
|
24
|
+
buildTier3Report,
|
|
25
|
+
formatComparison,
|
|
26
|
+
formatTier3Comparison,
|
|
27
|
+
} from "./benchmark/reporter";
|
|
28
|
+
export { parseTestOutput, runBenchmark } from "./benchmark/runner";
|
|
29
|
+
export { listStories, loadStory } from "./benchmark/story-loader";
|
|
30
|
+
// Benchmark
|
|
31
|
+
export type {
|
|
32
|
+
BenchmarkMetrics,
|
|
33
|
+
BenchmarkReport,
|
|
34
|
+
LoadedStory,
|
|
35
|
+
StepMetrics,
|
|
36
|
+
StoryConfig,
|
|
37
|
+
Tier3Results,
|
|
38
|
+
Tier3Totals,
|
|
39
|
+
} from "./benchmark/types";
|
|
40
|
+
// Cache
|
|
41
|
+
export {
|
|
42
|
+
type CacheManager,
|
|
43
|
+
type CacheStats,
|
|
44
|
+
createCacheManager,
|
|
45
|
+
} from "./cache/manager";
|
|
46
|
+
// Config
|
|
47
|
+
export { getApiKey, isHostMode, shouldDelegateToHost } from "./config/index";
|
|
48
|
+
export { calculateTokens } from "./context/budget";
|
|
49
|
+
export {
|
|
50
|
+
type AssembledContext,
|
|
51
|
+
assembleContext,
|
|
52
|
+
type ContextOptions,
|
|
53
|
+
type LayerReport,
|
|
54
|
+
} from "./context/engine";
|
|
55
|
+
// Context — episodic
|
|
56
|
+
export { addEntry as addEpisodicEntry } from "./context/episodic";
|
|
57
|
+
export type { MainaCommand } from "./context/selector";
|
|
58
|
+
// Context — working
|
|
59
|
+
export {
|
|
60
|
+
loadWorkingContext,
|
|
61
|
+
saveWorkingContext,
|
|
62
|
+
setVerificationResult,
|
|
63
|
+
trackFile,
|
|
64
|
+
} from "./context/working";
|
|
65
|
+
// DB (Result type)
|
|
66
|
+
export type { Result } from "./db/index";
|
|
67
|
+
// Design (ADR)
|
|
68
|
+
export {
|
|
69
|
+
type AdrSummary,
|
|
70
|
+
generateHldLld,
|
|
71
|
+
getNextAdrNumber,
|
|
72
|
+
listAdrs,
|
|
73
|
+
scaffoldAdr,
|
|
74
|
+
} from "./design/index";
|
|
75
|
+
// Design Review
|
|
76
|
+
export {
|
|
77
|
+
buildReviewContext,
|
|
78
|
+
findAdrByNumber,
|
|
79
|
+
type ReviewContext,
|
|
80
|
+
type ReviewFinding,
|
|
81
|
+
type ReviewOptions,
|
|
82
|
+
type ReviewResult,
|
|
83
|
+
reviewDesign,
|
|
84
|
+
} from "./design/review";
|
|
85
|
+
// Explain
|
|
86
|
+
export {
|
|
87
|
+
type DiagramOptions,
|
|
88
|
+
generateDependencyDiagram,
|
|
89
|
+
generateModuleSummary,
|
|
90
|
+
type ModuleSummary,
|
|
91
|
+
} from "./explain/index";
|
|
92
|
+
// Features
|
|
93
|
+
export {
|
|
94
|
+
type AnalysisFinding,
|
|
95
|
+
type AnalysisReport,
|
|
96
|
+
analyze,
|
|
97
|
+
} from "./features/analyzer";
|
|
98
|
+
export {
|
|
99
|
+
type CheckResult,
|
|
100
|
+
type VerificationReport,
|
|
101
|
+
verifyPlan,
|
|
102
|
+
} from "./features/checklist";
|
|
103
|
+
export {
|
|
104
|
+
createFeatureDir,
|
|
105
|
+
type DesignChoices,
|
|
106
|
+
getNextFeatureNumber,
|
|
107
|
+
scaffoldFeature,
|
|
108
|
+
scaffoldFeatureWithContext,
|
|
109
|
+
} from "./features/numbering";
|
|
110
|
+
export { type QualityScore, scoreSpec } from "./features/quality";
|
|
111
|
+
export { generateTestStubs } from "./features/test-stubs";
|
|
112
|
+
export type {
|
|
113
|
+
TaskTrace,
|
|
114
|
+
TraceabilityReport,
|
|
115
|
+
TraceDeps,
|
|
116
|
+
} from "./features/traceability";
|
|
117
|
+
export { traceFeature } from "./features/traceability";
|
|
118
|
+
// Feedback
|
|
119
|
+
export {
|
|
120
|
+
type FeedbackRecord,
|
|
121
|
+
getFeedbackSummary,
|
|
122
|
+
getWorkflowId,
|
|
123
|
+
recordFeedback,
|
|
124
|
+
recordFeedbackAsync,
|
|
125
|
+
recordFeedbackWithCompression,
|
|
126
|
+
} from "./feedback/collector";
|
|
127
|
+
export {
|
|
128
|
+
compressReview,
|
|
129
|
+
storeCompressedReview,
|
|
130
|
+
} from "./feedback/compress";
|
|
131
|
+
export {
|
|
132
|
+
acknowledgeFinding,
|
|
133
|
+
dismissFinding,
|
|
134
|
+
getNoisyRules,
|
|
135
|
+
loadPreferences,
|
|
136
|
+
type Preferences,
|
|
137
|
+
type RulePreference,
|
|
138
|
+
savePreferences,
|
|
139
|
+
} from "./feedback/preferences";
|
|
140
|
+
// Git
|
|
141
|
+
export {
|
|
142
|
+
type Commit,
|
|
143
|
+
getBranchName,
|
|
144
|
+
getChangedFiles,
|
|
145
|
+
getCurrentBranch,
|
|
146
|
+
getDiff,
|
|
147
|
+
getRecentCommits,
|
|
148
|
+
getRepoRoot,
|
|
149
|
+
getStagedFiles,
|
|
150
|
+
getTrackedFiles,
|
|
151
|
+
} from "./git/index";
|
|
152
|
+
// Hooks
|
|
153
|
+
export {
|
|
154
|
+
executeHook,
|
|
155
|
+
type HookContext,
|
|
156
|
+
type HookEvent,
|
|
157
|
+
type HookResult,
|
|
158
|
+
runHooks,
|
|
159
|
+
scanHooks,
|
|
160
|
+
} from "./hooks/index";
|
|
161
|
+
// Init
|
|
162
|
+
export {
|
|
163
|
+
bootstrap,
|
|
164
|
+
type DetectedStack,
|
|
165
|
+
type InitOptions,
|
|
166
|
+
type InitReport,
|
|
167
|
+
} from "./init/index";
|
|
168
|
+
// Language
|
|
169
|
+
export {
|
|
170
|
+
detectLanguages,
|
|
171
|
+
getPrimaryLanguage,
|
|
172
|
+
} from "./language/detect";
|
|
173
|
+
export {
|
|
174
|
+
GO_PROFILE,
|
|
175
|
+
getProfile,
|
|
176
|
+
getSupportedLanguages,
|
|
177
|
+
type LanguageId,
|
|
178
|
+
type LanguageProfile,
|
|
179
|
+
PYTHON_PROFILE,
|
|
180
|
+
RUST_PROFILE,
|
|
181
|
+
TYPESCRIPT_PROFILE,
|
|
182
|
+
} from "./language/profile";
|
|
183
|
+
export { loadDefault, type PromptTask } from "./prompts/defaults/index";
|
|
184
|
+
// Prompts
|
|
185
|
+
export {
|
|
186
|
+
type BuiltPrompt,
|
|
187
|
+
buildSystemPrompt,
|
|
188
|
+
type FeedbackOutcome,
|
|
189
|
+
getPromptStats,
|
|
190
|
+
type PromptStat,
|
|
191
|
+
recordOutcome,
|
|
192
|
+
} from "./prompts/engine";
|
|
193
|
+
export {
|
|
194
|
+
type ABResolution,
|
|
195
|
+
abTest,
|
|
196
|
+
analyseFeedback,
|
|
197
|
+
analyseWorkflowFeedback,
|
|
198
|
+
analyseWorkflowRuns,
|
|
199
|
+
type CandidatePrompt,
|
|
200
|
+
createCandidate,
|
|
201
|
+
type FeedbackAnalysis,
|
|
202
|
+
promote,
|
|
203
|
+
resolveABTests,
|
|
204
|
+
retire,
|
|
205
|
+
type WorkflowRunSummary,
|
|
206
|
+
type WorkflowStepAnalysis,
|
|
207
|
+
} from "./prompts/evolution";
|
|
208
|
+
// Comprehensive Review (Superpowers-style)
|
|
209
|
+
export {
|
|
210
|
+
type ComprehensiveReviewFinding,
|
|
211
|
+
type ComprehensiveReviewOptions,
|
|
212
|
+
type ComprehensiveReviewResult,
|
|
213
|
+
comprehensiveReview,
|
|
214
|
+
type ReviewSeverity,
|
|
215
|
+
} from "./review/comprehensive";
|
|
216
|
+
// PR Review (two-stage)
|
|
217
|
+
export {
|
|
218
|
+
type ReviewFinding as PrReviewFinding,
|
|
219
|
+
type ReviewOptions as PrReviewOptions,
|
|
220
|
+
type ReviewResult as PrReviewResult,
|
|
221
|
+
type ReviewStageResult,
|
|
222
|
+
reviewCodeQuality,
|
|
223
|
+
reviewCodeQualityWithAI,
|
|
224
|
+
reviewSpecCompliance,
|
|
225
|
+
runTwoStageReview,
|
|
226
|
+
} from "./review/index";
|
|
227
|
+
// Stats
|
|
228
|
+
export {
|
|
229
|
+
type CommitSnapshot,
|
|
230
|
+
type ComparisonReport,
|
|
231
|
+
getComparison,
|
|
232
|
+
getLatest,
|
|
233
|
+
getSkipRate,
|
|
234
|
+
getStats,
|
|
235
|
+
getTrends,
|
|
236
|
+
recordSnapshot,
|
|
237
|
+
type SnapshotInput,
|
|
238
|
+
type StatsReport,
|
|
239
|
+
type TrendDirection,
|
|
240
|
+
type TrendsReport,
|
|
241
|
+
} from "./stats/tracker";
|
|
242
|
+
// Ticket
|
|
243
|
+
export {
|
|
244
|
+
buildIssueBody,
|
|
245
|
+
createTicket,
|
|
246
|
+
detectModules,
|
|
247
|
+
type SpawnDeps,
|
|
248
|
+
type TicketOptions,
|
|
249
|
+
type TicketResult,
|
|
250
|
+
} from "./ticket/index";
|
|
251
|
+
// Utils
|
|
252
|
+
export { toKebabCase } from "./utils";
|
|
253
|
+
// Verify — AI Review
|
|
254
|
+
export {
|
|
255
|
+
type AIReviewOptions,
|
|
256
|
+
type AIReviewResult,
|
|
257
|
+
type EntityWithBody,
|
|
258
|
+
type ReferencedFunction,
|
|
259
|
+
resolveReferencedFunctions,
|
|
260
|
+
runAIReview,
|
|
261
|
+
} from "./verify/ai-review";
|
|
262
|
+
// Verify — Coverage
|
|
263
|
+
export {
|
|
264
|
+
type CoverageOptions,
|
|
265
|
+
type CoverageResult,
|
|
266
|
+
parseDiffCoverJson,
|
|
267
|
+
runCoverage,
|
|
268
|
+
} from "./verify/coverage";
|
|
269
|
+
export {
|
|
270
|
+
type DetectedTool,
|
|
271
|
+
detectTool,
|
|
272
|
+
detectTools,
|
|
273
|
+
isToolAvailable,
|
|
274
|
+
TOOL_REGISTRY,
|
|
275
|
+
type ToolName,
|
|
276
|
+
} from "./verify/detect";
|
|
277
|
+
export {
|
|
278
|
+
type DiffFilterResult,
|
|
279
|
+
type Finding,
|
|
280
|
+
filterByDiff,
|
|
281
|
+
filterByDiffWithMap,
|
|
282
|
+
parseChangedLines,
|
|
283
|
+
} from "./verify/diff-filter";
|
|
284
|
+
export {
|
|
285
|
+
type FixOptions,
|
|
286
|
+
type FixResult,
|
|
287
|
+
type FixSuggestion,
|
|
288
|
+
generateFixes,
|
|
289
|
+
hashFinding,
|
|
290
|
+
parseFixResponse,
|
|
291
|
+
} from "./verify/fix";
|
|
292
|
+
// Verify — Mutation
|
|
293
|
+
export {
|
|
294
|
+
type MutationOptions,
|
|
295
|
+
type MutationResult,
|
|
296
|
+
parseStrykerReport,
|
|
297
|
+
runMutation,
|
|
298
|
+
} from "./verify/mutation";
|
|
299
|
+
// Verify — Pipeline
|
|
300
|
+
export {
|
|
301
|
+
type PipelineOptions,
|
|
302
|
+
type PipelineResult,
|
|
303
|
+
runPipeline,
|
|
304
|
+
type ToolReport,
|
|
305
|
+
} from "./verify/pipeline";
|
|
306
|
+
// Verify — Proof
|
|
307
|
+
export {
|
|
308
|
+
formatVerificationProof,
|
|
309
|
+
gatherVerificationProof,
|
|
310
|
+
type ProofOptions,
|
|
311
|
+
type ToolProof,
|
|
312
|
+
type VerificationProof,
|
|
313
|
+
} from "./verify/proof";
|
|
314
|
+
export {
|
|
315
|
+
detectCommentedCode,
|
|
316
|
+
detectConsoleLogs,
|
|
317
|
+
detectEmptyBodies,
|
|
318
|
+
detectHallucinatedImports,
|
|
319
|
+
detectSlop,
|
|
320
|
+
detectTodosWithoutTickets,
|
|
321
|
+
type SlopResult,
|
|
322
|
+
type SlopRule,
|
|
323
|
+
} from "./verify/slop";
|
|
324
|
+
// Verify — SonarQube
|
|
325
|
+
export {
|
|
326
|
+
parseSonarReport,
|
|
327
|
+
runSonar,
|
|
328
|
+
type SonarOptions,
|
|
329
|
+
type SonarResult,
|
|
330
|
+
} from "./verify/sonar";
|
|
331
|
+
export {
|
|
332
|
+
parseBiomeOutput,
|
|
333
|
+
type SyntaxDiagnostic,
|
|
334
|
+
type SyntaxGuardResult,
|
|
335
|
+
syntaxGuard,
|
|
336
|
+
} from "./verify/syntax-guard";
|
|
337
|
+
// Verify — Visual
|
|
338
|
+
export {
|
|
339
|
+
captureScreenshot,
|
|
340
|
+
compareImages,
|
|
341
|
+
detectWebProject,
|
|
342
|
+
loadVisualConfig,
|
|
343
|
+
runVisualVerification,
|
|
344
|
+
type ScreenshotOptions,
|
|
345
|
+
type ScreenshotResult,
|
|
346
|
+
updateBaselines,
|
|
347
|
+
type VisualConfig,
|
|
348
|
+
type VisualDiffResult,
|
|
349
|
+
type VisualVerifyResult,
|
|
350
|
+
} from "./verify/visual";
|
|
351
|
+
// Workflow
|
|
352
|
+
export {
|
|
353
|
+
appendWorkflowStep,
|
|
354
|
+
loadWorkflowContext,
|
|
355
|
+
resetWorkflowContext,
|
|
356
|
+
} from "./workflow/context";
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { bootstrap } from "../index";
|
|
12
|
+
|
|
13
|
+
function makeTmpDir(): string {
|
|
14
|
+
const dir = join(
|
|
15
|
+
tmpdir(),
|
|
16
|
+
`maina-init-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
17
|
+
);
|
|
18
|
+
mkdirSync(dir, { recursive: true });
|
|
19
|
+
return dir;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("bootstrap", () => {
|
|
23
|
+
let tmpDir: string;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
tmpDir = makeTmpDir();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("creates .maina/ directory", async () => {
|
|
34
|
+
const result = await bootstrap(tmpDir);
|
|
35
|
+
expect(result.ok).toBe(true);
|
|
36
|
+
if (result.ok) {
|
|
37
|
+
expect(existsSync(join(tmpDir, ".maina"))).toBe(true);
|
|
38
|
+
expect(result.value.directory).toBe(join(tmpDir, ".maina"));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("creates constitution.md with template", async () => {
|
|
43
|
+
const result = await bootstrap(tmpDir);
|
|
44
|
+
expect(result.ok).toBe(true);
|
|
45
|
+
|
|
46
|
+
const constitutionPath = join(tmpDir, ".maina", "constitution.md");
|
|
47
|
+
expect(existsSync(constitutionPath)).toBe(true);
|
|
48
|
+
|
|
49
|
+
const content = readFileSync(constitutionPath, "utf-8");
|
|
50
|
+
expect(content).toContain("# Project Constitution");
|
|
51
|
+
expect(content).toContain("## Stack");
|
|
52
|
+
expect(content).toContain("## Architecture");
|
|
53
|
+
expect(content).toContain("## Verification");
|
|
54
|
+
expect(content).toContain("[NEEDS CLARIFICATION]");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("creates AGENTS.md at repo root", async () => {
|
|
58
|
+
const result = await bootstrap(tmpDir);
|
|
59
|
+
expect(result.ok).toBe(true);
|
|
60
|
+
|
|
61
|
+
const agentsPath = join(tmpDir, "AGENTS.md");
|
|
62
|
+
expect(existsSync(agentsPath)).toBe(true);
|
|
63
|
+
|
|
64
|
+
const content = readFileSync(agentsPath, "utf-8");
|
|
65
|
+
expect(content).toContain("# AGENTS.md");
|
|
66
|
+
expect(content).toContain("maina verify");
|
|
67
|
+
expect(content).toContain("maina commit");
|
|
68
|
+
expect(content).toContain("maina context");
|
|
69
|
+
expect(content).toContain("maina doctor");
|
|
70
|
+
expect(content).toContain("constitution.md");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("creates CI workflow", async () => {
|
|
74
|
+
const result = await bootstrap(tmpDir);
|
|
75
|
+
expect(result.ok).toBe(true);
|
|
76
|
+
|
|
77
|
+
const ciPath = join(tmpDir, ".github", "workflows", "maina-ci.yml");
|
|
78
|
+
expect(existsSync(ciPath)).toBe(true);
|
|
79
|
+
|
|
80
|
+
const content = readFileSync(ciPath, "utf-8");
|
|
81
|
+
expect(content).toContain("name: Maina CI");
|
|
82
|
+
expect(content).toContain("actions/checkout@v4");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("detects bun stack from package.json", async () => {
|
|
86
|
+
// Create a Bun project
|
|
87
|
+
writeFileSync(
|
|
88
|
+
join(tmpDir, "package.json"),
|
|
89
|
+
JSON.stringify({ devDependencies: { "@types/bun": "latest" } }),
|
|
90
|
+
);
|
|
91
|
+
writeFileSync(join(tmpDir, "tsconfig.json"), "{}");
|
|
92
|
+
|
|
93
|
+
const result = await bootstrap(tmpDir);
|
|
94
|
+
expect(result.ok).toBe(true);
|
|
95
|
+
if (result.ok) {
|
|
96
|
+
expect(result.value.detectedStack.runtime).toBe("bun");
|
|
97
|
+
expect(result.value.detectedStack.language).toBe("typescript");
|
|
98
|
+
|
|
99
|
+
const ci = readFileSync(
|
|
100
|
+
join(tmpDir, ".github", "workflows", "maina-ci.yml"),
|
|
101
|
+
"utf-8",
|
|
102
|
+
);
|
|
103
|
+
expect(ci).toContain("oven-sh/setup-bun@v2");
|
|
104
|
+
expect(ci).toContain("bun install");
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("creates prompts directory with defaults", async () => {
|
|
109
|
+
const result = await bootstrap(tmpDir);
|
|
110
|
+
expect(result.ok).toBe(true);
|
|
111
|
+
|
|
112
|
+
const promptsDir = join(tmpDir, ".maina", "prompts");
|
|
113
|
+
expect(existsSync(promptsDir)).toBe(true);
|
|
114
|
+
|
|
115
|
+
const reviewPath = join(promptsDir, "review.md");
|
|
116
|
+
expect(existsSync(reviewPath)).toBe(true);
|
|
117
|
+
const reviewContent = readFileSync(reviewPath, "utf-8");
|
|
118
|
+
expect(reviewContent.length).toBeGreaterThan(0);
|
|
119
|
+
|
|
120
|
+
const commitPath = join(promptsDir, "commit.md");
|
|
121
|
+
expect(existsSync(commitPath)).toBe(true);
|
|
122
|
+
const commitContent = readFileSync(commitPath, "utf-8");
|
|
123
|
+
expect(commitContent.length).toBeGreaterThan(0);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("creates hooks directory", async () => {
|
|
127
|
+
const result = await bootstrap(tmpDir);
|
|
128
|
+
expect(result.ok).toBe(true);
|
|
129
|
+
|
|
130
|
+
const hooksDir = join(tmpDir, ".maina", "hooks");
|
|
131
|
+
expect(existsSync(hooksDir)).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("skips existing files (no overwrite)", async () => {
|
|
135
|
+
// Pre-create constitution.md with custom content
|
|
136
|
+
const mainaDir = join(tmpDir, ".maina");
|
|
137
|
+
mkdirSync(mainaDir, { recursive: true });
|
|
138
|
+
const constitutionPath = join(mainaDir, "constitution.md");
|
|
139
|
+
writeFileSync(constitutionPath, "# My Custom Constitution\n");
|
|
140
|
+
|
|
141
|
+
// Pre-create AGENTS.md with custom content
|
|
142
|
+
const agentsPath = join(tmpDir, "AGENTS.md");
|
|
143
|
+
writeFileSync(agentsPath, "# My Custom Agents\n");
|
|
144
|
+
|
|
145
|
+
const result = await bootstrap(tmpDir);
|
|
146
|
+
expect(result.ok).toBe(true);
|
|
147
|
+
if (result.ok) {
|
|
148
|
+
expect(result.value.skipped).toContain(".maina/constitution.md");
|
|
149
|
+
expect(result.value.skipped).toContain("AGENTS.md");
|
|
150
|
+
|
|
151
|
+
// Content should NOT have been overwritten
|
|
152
|
+
const content = readFileSync(constitutionPath, "utf-8");
|
|
153
|
+
expect(content).toBe("# My Custom Constitution\n");
|
|
154
|
+
|
|
155
|
+
const agentsContent = readFileSync(agentsPath, "utf-8");
|
|
156
|
+
expect(agentsContent).toBe("# My Custom Agents\n");
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("force flag overwrites existing", async () => {
|
|
161
|
+
// Pre-create constitution.md with custom content
|
|
162
|
+
const mainaDir = join(tmpDir, ".maina");
|
|
163
|
+
mkdirSync(mainaDir, { recursive: true });
|
|
164
|
+
const constitutionPath = join(mainaDir, "constitution.md");
|
|
165
|
+
writeFileSync(constitutionPath, "# My Custom Constitution\n");
|
|
166
|
+
|
|
167
|
+
const result = await bootstrap(tmpDir, { force: true });
|
|
168
|
+
expect(result.ok).toBe(true);
|
|
169
|
+
if (result.ok) {
|
|
170
|
+
expect(result.value.created).toContain(".maina/constitution.md");
|
|
171
|
+
expect(result.value.skipped).not.toContain(".maina/constitution.md");
|
|
172
|
+
|
|
173
|
+
// Content should have been overwritten with template
|
|
174
|
+
const content = readFileSync(constitutionPath, "utf-8");
|
|
175
|
+
expect(content).toContain("# Project Constitution");
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("returns correct created/skipped lists", async () => {
|
|
180
|
+
// Pre-create one file
|
|
181
|
+
const mainaDir = join(tmpDir, ".maina");
|
|
182
|
+
mkdirSync(mainaDir, { recursive: true });
|
|
183
|
+
writeFileSync(join(mainaDir, "constitution.md"), "existing\n");
|
|
184
|
+
|
|
185
|
+
const result = await bootstrap(tmpDir);
|
|
186
|
+
expect(result.ok).toBe(true);
|
|
187
|
+
if (result.ok) {
|
|
188
|
+
// constitution.md should be skipped
|
|
189
|
+
expect(result.value.skipped).toContain(".maina/constitution.md");
|
|
190
|
+
expect(result.value.created).not.toContain(".maina/constitution.md");
|
|
191
|
+
|
|
192
|
+
// Other files should be created
|
|
193
|
+
expect(result.value.created).toContain("AGENTS.md");
|
|
194
|
+
expect(result.value.created).toContain(".github/workflows/maina-ci.yml");
|
|
195
|
+
expect(result.value.created).toContain(".maina/prompts/review.md");
|
|
196
|
+
expect(result.value.created).toContain(".maina/prompts/commit.md");
|
|
197
|
+
|
|
198
|
+
// Total files should add up
|
|
199
|
+
const total = result.value.created.length + result.value.skipped.length;
|
|
200
|
+
expect(total).toBeGreaterThanOrEqual(5);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("works on empty directory", async () => {
|
|
205
|
+
// tmpDir is already empty
|
|
206
|
+
const result = await bootstrap(tmpDir);
|
|
207
|
+
expect(result.ok).toBe(true);
|
|
208
|
+
if (result.ok) {
|
|
209
|
+
expect(result.value.created.length).toBeGreaterThanOrEqual(5);
|
|
210
|
+
expect(result.value.skipped.length).toBe(0);
|
|
211
|
+
expect(result.value.directory).toBe(join(tmpDir, ".maina"));
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("detects available verification tools in report", async () => {
|
|
216
|
+
const result = await bootstrap(tmpDir);
|
|
217
|
+
expect(result.ok).toBe(true);
|
|
218
|
+
if (result.ok) {
|
|
219
|
+
expect(result.value.detectedTools).toBeDefined();
|
|
220
|
+
expect(Array.isArray(result.value.detectedTools)).toBe(true);
|
|
221
|
+
// Each tool should have name and available flag
|
|
222
|
+
for (const tool of result.value.detectedTools) {
|
|
223
|
+
expect(typeof tool.name).toBe("string");
|
|
224
|
+
expect(typeof tool.available).toBe("boolean");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
});
|