thrivekit 2.0.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/.claude/commands/explain.md +114 -0
- package/.claude/commands/idea.md +370 -0
- package/.claude/commands/my-dna.md +122 -0
- package/.claude/commands/prd.md +286 -0
- package/.claude/commands/review.md +167 -0
- package/.claude/commands/sign.md +32 -0
- package/.claude/commands/styleguide.md +450 -0
- package/.claude/commands/tour.md +301 -0
- package/.claude/commands/vibe-check.md +116 -0
- package/.claude/commands/vibe-help.md +47 -0
- package/.claude/commands/vibe-list.md +203 -0
- package/.claude/settings.json +75 -0
- package/.claude/settings.local.json +12 -0
- package/.pre-commit-hooks.yaml +102 -0
- package/LICENSE +21 -0
- package/README.md +214 -0
- package/bin/postinstall.sh +29 -0
- package/bin/ralph.sh +171 -0
- package/bin/thrivekit.sh +24 -0
- package/bin/vibe-check.js +19 -0
- package/dist/checks/check-any-types.d.ts +6 -0
- package/dist/checks/check-any-types.d.ts.map +1 -0
- package/dist/checks/check-any-types.js +73 -0
- package/dist/checks/check-any-types.js.map +1 -0
- package/dist/checks/check-commented-code.d.ts +6 -0
- package/dist/checks/check-commented-code.d.ts.map +1 -0
- package/dist/checks/check-commented-code.js +81 -0
- package/dist/checks/check-commented-code.js.map +1 -0
- package/dist/checks/check-console-error.d.ts +6 -0
- package/dist/checks/check-console-error.d.ts.map +1 -0
- package/dist/checks/check-console-error.js +41 -0
- package/dist/checks/check-console-error.js.map +1 -0
- package/dist/checks/check-debug-statements.d.ts +6 -0
- package/dist/checks/check-debug-statements.d.ts.map +1 -0
- package/dist/checks/check-debug-statements.js +120 -0
- package/dist/checks/check-debug-statements.js.map +1 -0
- package/dist/checks/check-deep-nesting.d.ts +6 -0
- package/dist/checks/check-deep-nesting.d.ts.map +1 -0
- package/dist/checks/check-deep-nesting.js +116 -0
- package/dist/checks/check-deep-nesting.js.map +1 -0
- package/dist/checks/check-docker-platform.d.ts +6 -0
- package/dist/checks/check-docker-platform.d.ts.map +1 -0
- package/dist/checks/check-docker-platform.js +42 -0
- package/dist/checks/check-docker-platform.js.map +1 -0
- package/dist/checks/check-dry-violations.d.ts +6 -0
- package/dist/checks/check-dry-violations.d.ts.map +1 -0
- package/dist/checks/check-dry-violations.js +124 -0
- package/dist/checks/check-dry-violations.js.map +1 -0
- package/dist/checks/check-empty-catch.d.ts +6 -0
- package/dist/checks/check-empty-catch.d.ts.map +1 -0
- package/dist/checks/check-empty-catch.js +111 -0
- package/dist/checks/check-empty-catch.js.map +1 -0
- package/dist/checks/check-function-length.d.ts +6 -0
- package/dist/checks/check-function-length.d.ts.map +1 -0
- package/dist/checks/check-function-length.js +152 -0
- package/dist/checks/check-function-length.js.map +1 -0
- package/dist/checks/check-hardcoded-ai-models.d.ts +10 -0
- package/dist/checks/check-hardcoded-ai-models.d.ts.map +1 -0
- package/dist/checks/check-hardcoded-ai-models.js +102 -0
- package/dist/checks/check-hardcoded-ai-models.js.map +1 -0
- package/dist/checks/check-hardcoded-urls.d.ts +6 -0
- package/dist/checks/check-hardcoded-urls.d.ts.map +1 -0
- package/dist/checks/check-hardcoded-urls.js +124 -0
- package/dist/checks/check-hardcoded-urls.js.map +1 -0
- package/dist/checks/check-magic-numbers.d.ts +6 -0
- package/dist/checks/check-magic-numbers.d.ts.map +1 -0
- package/dist/checks/check-magic-numbers.js +116 -0
- package/dist/checks/check-magic-numbers.js.map +1 -0
- package/dist/checks/check-secrets.d.ts +6 -0
- package/dist/checks/check-secrets.d.ts.map +1 -0
- package/dist/checks/check-secrets.js +138 -0
- package/dist/checks/check-secrets.js.map +1 -0
- package/dist/checks/check-snake-case-ts.d.ts +6 -0
- package/dist/checks/check-snake-case-ts.d.ts.map +1 -0
- package/dist/checks/check-snake-case-ts.js +78 -0
- package/dist/checks/check-snake-case-ts.js.map +1 -0
- package/dist/checks/check-todo-fixme.d.ts +6 -0
- package/dist/checks/check-todo-fixme.d.ts.map +1 -0
- package/dist/checks/check-todo-fixme.js +41 -0
- package/dist/checks/check-todo-fixme.js.map +1 -0
- package/dist/checks/check-unsafe-html.d.ts +6 -0
- package/dist/checks/check-unsafe-html.d.ts.map +1 -0
- package/dist/checks/check-unsafe-html.js +101 -0
- package/dist/checks/check-unsafe-html.js.map +1 -0
- package/dist/checks/index.d.ts +30 -0
- package/dist/checks/index.d.ts.map +1 -0
- package/dist/checks/index.js +57 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +206 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/file-reader.d.ts +24 -0
- package/dist/utils/file-reader.d.ts.map +1 -0
- package/dist/utils/file-reader.js +140 -0
- package/dist/utils/file-reader.js.map +1 -0
- package/dist/utils/patterns.d.ts +27 -0
- package/dist/utils/patterns.d.ts.map +1 -0
- package/dist/utils/patterns.js +84 -0
- package/dist/utils/patterns.js.map +1 -0
- package/dist/utils/reporters.d.ts +21 -0
- package/dist/utils/reporters.d.ts.map +1 -0
- package/dist/utils/reporters.js +115 -0
- package/dist/utils/reporters.js.map +1 -0
- package/dist/utils/types.d.ts +71 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +5 -0
- package/dist/utils/types.js.map +1 -0
- package/package.json +82 -0
- package/ralph/api.sh +210 -0
- package/ralph/backup.sh +838 -0
- package/ralph/browser-verify/README.md +135 -0
- package/ralph/browser-verify/verify.ts +450 -0
- package/ralph/checks/check-fastapi-responses.py +155 -0
- package/ralph/hooks/hooks-config.json +72 -0
- package/ralph/hooks/inject-context.sh +44 -0
- package/ralph/hooks/install.sh +207 -0
- package/ralph/hooks/log-tools.sh +45 -0
- package/ralph/hooks/protect-prd.sh +27 -0
- package/ralph/hooks/save-learnings.sh +36 -0
- package/ralph/hooks/warn-debug.sh +54 -0
- package/ralph/hooks/warn-empty-catch.sh +63 -0
- package/ralph/hooks/warn-secrets.sh +89 -0
- package/ralph/hooks/warn-urls.sh +77 -0
- package/ralph/init.sh +388 -0
- package/ralph/loop.sh +570 -0
- package/ralph/playwright.sh +238 -0
- package/ralph/prd.sh +295 -0
- package/ralph/setup/feature-tour.sh +155 -0
- package/ralph/setup/quick-setup.sh +239 -0
- package/ralph/setup/tutorial.sh +159 -0
- package/ralph/setup/ui.sh +136 -0
- package/ralph/setup.sh +353 -0
- package/ralph/signs.sh +150 -0
- package/ralph/utils.sh +682 -0
- package/ralph/verify/browser.sh +324 -0
- package/ralph/verify/lint.sh +363 -0
- package/ralph/verify/review.sh +164 -0
- package/ralph/verify/tests.sh +81 -0
- package/ralph/verify.sh +224 -0
- package/templates/PROMPT.md +235 -0
- package/templates/config/fullstack.json +86 -0
- package/templates/config/go.json +81 -0
- package/templates/config/minimal.json +76 -0
- package/templates/config/node.json +81 -0
- package/templates/config/python.json +81 -0
- package/templates/config/rust.json +81 -0
- package/templates/examples/CLAUDE-django.md +174 -0
- package/templates/examples/CLAUDE-fastapi.md +270 -0
- package/templates/examples/CLAUDE-fastmcp.md +352 -0
- package/templates/examples/CLAUDE-fullstack.md +256 -0
- package/templates/examples/CLAUDE-node.md +246 -0
- package/templates/examples/CLAUDE-react.md +138 -0
- package/templates/optional/cursorrules.template +147 -0
- package/templates/optional/eslint.config.js +34 -0
- package/templates/optional/lint-staged.config.js +34 -0
- package/templates/optional/ruff.toml +125 -0
- package/templates/optional/vibe-check.yml +116 -0
- package/templates/optional/vscode-settings.json +127 -0
- package/templates/signs.json +46 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common regex patterns used across hooks
|
|
3
|
+
*/
|
|
4
|
+
export declare const SECRET_PATTERNS: {
|
|
5
|
+
awsAccessKey: RegExp;
|
|
6
|
+
awsSecretKey: RegExp;
|
|
7
|
+
genericApiKey: RegExp;
|
|
8
|
+
genericSecret: RegExp;
|
|
9
|
+
stripeKey: RegExp;
|
|
10
|
+
githubToken: RegExp;
|
|
11
|
+
slackToken: RegExp;
|
|
12
|
+
twilioKey: RegExp;
|
|
13
|
+
sendgridKey: RegExp;
|
|
14
|
+
privateKey: RegExp;
|
|
15
|
+
jwt: RegExp;
|
|
16
|
+
};
|
|
17
|
+
export declare const URL_PATTERNS: {
|
|
18
|
+
localhost: RegExp;
|
|
19
|
+
localIp: RegExp;
|
|
20
|
+
hardcodedUrl: RegExp;
|
|
21
|
+
};
|
|
22
|
+
export declare const DEBUG_PATTERNS: Record<string, RegExp[]>;
|
|
23
|
+
export declare const LANGUAGE_EXTENSIONS: Record<string, string[]>;
|
|
24
|
+
export declare function getLanguage(extension: string): string | undefined;
|
|
25
|
+
export declare const PLACEHOLDER_PATTERNS: RegExp[];
|
|
26
|
+
export declare function isPlaceholder(value: string): boolean;
|
|
27
|
+
//# sourceMappingURL=patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/utils/patterns.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,eAAO,MAAM,eAAe;;;;;;;;;;;;CAqB3B,CAAC;AAGF,eAAO,MAAM,YAAY;;;;CAOxB,CAAC;AAGF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAgBnD,CAAC;AAGF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAQxD,CAAC;AAGF,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOjE;AAGD,eAAO,MAAM,oBAAoB,UAUhC,CAAC;AAGF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common regex patterns used across hooks
|
|
3
|
+
*/
|
|
4
|
+
// Secret patterns
|
|
5
|
+
export const SECRET_PATTERNS = {
|
|
6
|
+
// AWS
|
|
7
|
+
awsAccessKey: /AKIA[0-9A-Z]{16}/,
|
|
8
|
+
awsSecretKey: /(?<![A-Za-z0-9/+=])[A-Za-z0-9/+=]{40}(?![A-Za-z0-9/+=])/,
|
|
9
|
+
// API keys (generic)
|
|
10
|
+
genericApiKey: /(?:api[_-]?key|apikey|api[_-]?secret)\s*[:=]\s*['"][a-zA-Z0-9_\-]{20,}['"]/i,
|
|
11
|
+
genericSecret: /(?:secret|password|passwd|pwd|token|auth)\s*[:=]\s*['"][^'"]{8,}['"]/i,
|
|
12
|
+
// Specific services
|
|
13
|
+
stripeKey: /sk_(?:live|test)_[0-9a-zA-Z]{24,}/,
|
|
14
|
+
githubToken: /gh[pousr]_[A-Za-z0-9_]{36,}/,
|
|
15
|
+
slackToken: /xox[baprs]-[0-9]{10,}-[0-9a-zA-Z]{24,}/,
|
|
16
|
+
twilioKey: /SK[0-9a-fA-F]{32}/,
|
|
17
|
+
sendgridKey: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/,
|
|
18
|
+
// Private keys
|
|
19
|
+
privateKey: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
|
|
20
|
+
// JWT (only if it looks like a real token, not a placeholder)
|
|
21
|
+
jwt: /eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/,
|
|
22
|
+
};
|
|
23
|
+
// URL patterns
|
|
24
|
+
export const URL_PATTERNS = {
|
|
25
|
+
// Localhost URLs
|
|
26
|
+
localhost: /https?:\/\/localhost(?::\d+)?(?:\/[^\s'"]*)?/g,
|
|
27
|
+
localIp: /https?:\/\/127\.0\.0\.1(?::\d+)?(?:\/[^\s'"]*)?/g,
|
|
28
|
+
// Hardcoded production URLs (excluding common CDNs)
|
|
29
|
+
hardcodedUrl: /https?:\/\/(?!(?:cdn|fonts|unpkg|cdnjs|jsdelivr)\.)[a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z]{2,}(?::\d+)?(?:\/[^\s'"]*)?/g,
|
|
30
|
+
};
|
|
31
|
+
// Debug statement patterns by language
|
|
32
|
+
export const DEBUG_PATTERNS = {
|
|
33
|
+
javascript: [
|
|
34
|
+
/console\.(log|debug|info|warn|error|trace|dir|table)\s*\(/,
|
|
35
|
+
/debugger\s*;?/,
|
|
36
|
+
/alert\s*\(/,
|
|
37
|
+
],
|
|
38
|
+
typescript: [
|
|
39
|
+
/console\.(log|debug|info|warn|error|trace|dir|table)\s*\(/,
|
|
40
|
+
/debugger\s*;?/,
|
|
41
|
+
],
|
|
42
|
+
python: [
|
|
43
|
+
/\bprint\s*\(/,
|
|
44
|
+
/\bbreakpoint\s*\(\s*\)/,
|
|
45
|
+
/\bpdb\.set_trace\s*\(\s*\)/,
|
|
46
|
+
/\bipdb\.set_trace\s*\(\s*\)/,
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
// File extensions by language type
|
|
50
|
+
export const LANGUAGE_EXTENSIONS = {
|
|
51
|
+
javascript: ['js', 'jsx', 'mjs', 'cjs'],
|
|
52
|
+
typescript: ['ts', 'tsx', 'mts', 'cts'],
|
|
53
|
+
python: ['py', 'pyw'],
|
|
54
|
+
json: ['json', 'jsonc'],
|
|
55
|
+
yaml: ['yaml', 'yml'],
|
|
56
|
+
docker: ['dockerfile'],
|
|
57
|
+
html: ['html', 'htm'],
|
|
58
|
+
};
|
|
59
|
+
// Get language from file extension
|
|
60
|
+
export function getLanguage(extension) {
|
|
61
|
+
for (const [lang, exts] of Object.entries(LANGUAGE_EXTENSIONS)) {
|
|
62
|
+
if (exts.includes(extension.toLowerCase())) {
|
|
63
|
+
return lang;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
// Common placeholder patterns (to ignore in secret detection)
|
|
69
|
+
export const PLACEHOLDER_PATTERNS = [
|
|
70
|
+
/example/i,
|
|
71
|
+
/placeholder/i,
|
|
72
|
+
/your[_-]?(?:api[_-]?)?key/i,
|
|
73
|
+
/xxx+/i,
|
|
74
|
+
/test/i,
|
|
75
|
+
/dummy/i,
|
|
76
|
+
/fake/i,
|
|
77
|
+
/sample/i,
|
|
78
|
+
/demo/i,
|
|
79
|
+
];
|
|
80
|
+
// Check if a string looks like a placeholder
|
|
81
|
+
export function isPlaceholder(value) {
|
|
82
|
+
return PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(value));
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/utils/patterns.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kBAAkB;AAClB,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM;IACN,YAAY,EAAE,kBAAkB;IAChC,YAAY,EAAE,yDAAyD;IAEvE,qBAAqB;IACrB,aAAa,EAAE,6EAA6E;IAC5F,aAAa,EAAE,uEAAuE;IAEtF,oBAAoB;IACpB,SAAS,EAAE,mCAAmC;IAC9C,WAAW,EAAE,6BAA6B;IAC1C,UAAU,EAAE,wCAAwC;IACpD,SAAS,EAAE,mBAAmB;IAC9B,WAAW,EAAE,0CAA0C;IAEvD,eAAe;IACf,UAAU,EAAE,wDAAwD;IAEpE,8DAA8D;IAC9D,GAAG,EAAE,kEAAkE;CACxE,CAAC;AAEF,eAAe;AACf,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,iBAAiB;IACjB,SAAS,EAAE,+CAA+C;IAC1D,OAAO,EAAE,kDAAkD;IAE3D,oDAAoD;IACpD,YAAY,EAAE,oHAAoH;CACnI,CAAC;AAEF,uCAAuC;AACvC,MAAM,CAAC,MAAM,cAAc,GAA6B;IACtD,UAAU,EAAE;QACV,2DAA2D;QAC3D,eAAe;QACf,YAAY;KACb;IACD,UAAU,EAAE;QACV,2DAA2D;QAC3D,eAAe;KAChB;IACD,MAAM,EAAE;QACN,cAAc;QACd,wBAAwB;QACxB,4BAA4B;QAC5B,6BAA6B;KAC9B;CACF,CAAC;AAEF,mCAAmC;AACnC,MAAM,CAAC,MAAM,mBAAmB,GAA6B;IAC3D,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;IACvC,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;IACvC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC;IACrB,MAAM,EAAE,CAAC,YAAY,CAAC;IACtB,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC;CACtB,CAAC;AAEF,mCAAmC;AACnC,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC/D,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,UAAU;IACV,cAAc;IACd,4BAA4B;IAC5B,OAAO;IACP,OAAO;IACP,QAAQ;IACR,OAAO;IACP,SAAS;IACT,OAAO;CACR,CAAC;AAEF,6CAA6C;AAC7C,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatters for CLI results
|
|
3
|
+
*/
|
|
4
|
+
import type { SummaryResult } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Pretty format - colored terminal output
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatPretty(summary: SummaryResult): string;
|
|
9
|
+
/**
|
|
10
|
+
* JSON format - machine-readable output
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatJson(summary: SummaryResult): string;
|
|
13
|
+
/**
|
|
14
|
+
* Compact format - one line per issue
|
|
15
|
+
*/
|
|
16
|
+
export declare function formatCompact(summary: SummaryResult): string;
|
|
17
|
+
/**
|
|
18
|
+
* Format results based on output format option
|
|
19
|
+
*/
|
|
20
|
+
export declare function formatResults(summary: SummaryResult, format: 'pretty' | 'json' | 'compact'): string;
|
|
21
|
+
//# sourceMappingURL=reporters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporters.d.ts","sourceRoot":"","sources":["../../src/utils/reporters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,YAAY,CAAC;AAoC1D;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAoD3D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAgB5D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,GACpC,MAAM,CASR"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatters for CLI results
|
|
3
|
+
*/
|
|
4
|
+
// ANSI color codes
|
|
5
|
+
const colors = {
|
|
6
|
+
reset: '\x1b[0m',
|
|
7
|
+
bold: '\x1b[1m',
|
|
8
|
+
dim: '\x1b[2m',
|
|
9
|
+
red: '\x1b[31m',
|
|
10
|
+
yellow: '\x1b[33m',
|
|
11
|
+
blue: '\x1b[34m',
|
|
12
|
+
cyan: '\x1b[36m',
|
|
13
|
+
gray: '\x1b[90m',
|
|
14
|
+
};
|
|
15
|
+
function severityColor(severity) {
|
|
16
|
+
switch (severity) {
|
|
17
|
+
case 'error':
|
|
18
|
+
return colors.red;
|
|
19
|
+
case 'warning':
|
|
20
|
+
return colors.yellow;
|
|
21
|
+
case 'info':
|
|
22
|
+
return colors.blue;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function severityIcon(severity) {
|
|
26
|
+
switch (severity) {
|
|
27
|
+
case 'error':
|
|
28
|
+
return '✖';
|
|
29
|
+
case 'warning':
|
|
30
|
+
return '⚠';
|
|
31
|
+
case 'info':
|
|
32
|
+
return 'ℹ';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Pretty format - colored terminal output
|
|
37
|
+
*/
|
|
38
|
+
export function formatPretty(summary) {
|
|
39
|
+
const lines = [];
|
|
40
|
+
// Header
|
|
41
|
+
lines.push('');
|
|
42
|
+
lines.push(`${colors.bold}vibe-check${colors.reset}`);
|
|
43
|
+
lines.push('');
|
|
44
|
+
// Results by file
|
|
45
|
+
for (const result of summary.results) {
|
|
46
|
+
if (result.issues.length === 0)
|
|
47
|
+
continue;
|
|
48
|
+
lines.push(`${colors.cyan}${result.filePath}${colors.reset}`);
|
|
49
|
+
for (const issue of result.issues) {
|
|
50
|
+
const color = severityColor(issue.severity);
|
|
51
|
+
const icon = severityIcon(issue.severity);
|
|
52
|
+
const location = issue.column
|
|
53
|
+
? `${issue.line}:${issue.column}`
|
|
54
|
+
: `${issue.line}`;
|
|
55
|
+
lines.push(` ${colors.gray}${location.padEnd(8)}${colors.reset}` +
|
|
56
|
+
`${color}${icon}${colors.reset} ` +
|
|
57
|
+
`${issue.message} ${colors.dim}${issue.ruleId}${colors.reset}`);
|
|
58
|
+
}
|
|
59
|
+
lines.push('');
|
|
60
|
+
}
|
|
61
|
+
// Summary
|
|
62
|
+
const problems = [];
|
|
63
|
+
if (summary.errorCount > 0) {
|
|
64
|
+
problems.push(`${colors.red}${summary.errorCount} error${summary.errorCount === 1 ? '' : 's'}${colors.reset}`);
|
|
65
|
+
}
|
|
66
|
+
if (summary.warningCount > 0) {
|
|
67
|
+
problems.push(`${colors.yellow}${summary.warningCount} warning${summary.warningCount === 1 ? '' : 's'}${colors.reset}`);
|
|
68
|
+
}
|
|
69
|
+
if (summary.infoCount > 0) {
|
|
70
|
+
problems.push(`${colors.blue}${summary.infoCount} info${colors.reset}`);
|
|
71
|
+
}
|
|
72
|
+
if (problems.length > 0) {
|
|
73
|
+
lines.push(`${problems.join(', ')} in ${summary.filesWithIssues} file${summary.filesWithIssues === 1 ? '' : 's'}`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
lines.push(`${colors.bold}✓${colors.reset} ${summary.filesChecked} file${summary.filesChecked === 1 ? '' : 's'} checked - no issues found`);
|
|
77
|
+
}
|
|
78
|
+
lines.push('');
|
|
79
|
+
return lines.join('\n');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* JSON format - machine-readable output
|
|
83
|
+
*/
|
|
84
|
+
export function formatJson(summary) {
|
|
85
|
+
return JSON.stringify(summary, null, 2);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Compact format - one line per issue
|
|
89
|
+
*/
|
|
90
|
+
export function formatCompact(summary) {
|
|
91
|
+
const lines = [];
|
|
92
|
+
for (const result of summary.results) {
|
|
93
|
+
for (const issue of result.issues) {
|
|
94
|
+
const location = issue.column
|
|
95
|
+
? `${issue.line}:${issue.column}`
|
|
96
|
+
: `${issue.line}`;
|
|
97
|
+
lines.push(`${result.filePath}:${location}: ${issue.severity}: ${issue.message} [${issue.ruleId}]`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Format results based on output format option
|
|
104
|
+
*/
|
|
105
|
+
export function formatResults(summary, format) {
|
|
106
|
+
switch (format) {
|
|
107
|
+
case 'pretty':
|
|
108
|
+
return formatPretty(summary);
|
|
109
|
+
case 'json':
|
|
110
|
+
return formatJson(summary);
|
|
111
|
+
case 'compact':
|
|
112
|
+
return formatCompact(summary);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=reporters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporters.js","sourceRoot":"","sources":["../../src/utils/reporters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for thrivekit
|
|
3
|
+
*/
|
|
4
|
+
export type Severity = 'error' | 'warning' | 'info';
|
|
5
|
+
export interface FileContext {
|
|
6
|
+
/** Absolute path to the file */
|
|
7
|
+
filePath: string;
|
|
8
|
+
/** File content as string */
|
|
9
|
+
content: string;
|
|
10
|
+
/** File extension without dot (e.g., 'ts', 'py') */
|
|
11
|
+
extension: string;
|
|
12
|
+
}
|
|
13
|
+
export interface HookResult {
|
|
14
|
+
/** 1-based line number */
|
|
15
|
+
line: number;
|
|
16
|
+
/** 0-based column number */
|
|
17
|
+
column?: number;
|
|
18
|
+
/** Human-readable message */
|
|
19
|
+
message: string;
|
|
20
|
+
/** Severity level */
|
|
21
|
+
severity: Severity;
|
|
22
|
+
/** Rule identifier (e.g., 'secrets/aws-key') */
|
|
23
|
+
ruleId: string;
|
|
24
|
+
/** Optional fix suggestion */
|
|
25
|
+
fix?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface Hook {
|
|
28
|
+
/** Unique identifier (e.g., 'secrets', 'debug-statements') */
|
|
29
|
+
id: string;
|
|
30
|
+
/** Human-readable name */
|
|
31
|
+
name: string;
|
|
32
|
+
/** Description of what this hook checks */
|
|
33
|
+
description: string;
|
|
34
|
+
/** Default severity level */
|
|
35
|
+
severity: Severity;
|
|
36
|
+
/** File extensions this hook applies to (without dots) */
|
|
37
|
+
fileTypes: string[];
|
|
38
|
+
/** Check function that returns issues found */
|
|
39
|
+
check: (context: FileContext) => HookResult[];
|
|
40
|
+
}
|
|
41
|
+
export interface CheckOptions {
|
|
42
|
+
/** Only run these hooks (by id) */
|
|
43
|
+
only?: string[];
|
|
44
|
+
/** Skip these hooks (by id) */
|
|
45
|
+
skip?: string[];
|
|
46
|
+
/** Minimum severity to fail on */
|
|
47
|
+
failOn?: Severity;
|
|
48
|
+
/** Output format */
|
|
49
|
+
format?: 'pretty' | 'json' | 'compact';
|
|
50
|
+
/** Auto-fix issues where possible */
|
|
51
|
+
fix?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface CheckResult {
|
|
54
|
+
/** File path */
|
|
55
|
+
filePath: string;
|
|
56
|
+
/** All issues found in this file */
|
|
57
|
+
issues: HookResult[];
|
|
58
|
+
}
|
|
59
|
+
export interface SummaryResult {
|
|
60
|
+
/** Total files checked */
|
|
61
|
+
filesChecked: number;
|
|
62
|
+
/** Files with issues */
|
|
63
|
+
filesWithIssues: number;
|
|
64
|
+
/** Total issues by severity */
|
|
65
|
+
errorCount: number;
|
|
66
|
+
warningCount: number;
|
|
67
|
+
infoCount: number;
|
|
68
|
+
/** All results */
|
|
69
|
+
results: CheckResult[];
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/utils/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,IAAI;IACnB,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,+CAA+C;IAC/C,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,UAAU,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,kCAAkC;IAClC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,oBAAoB;IACpB,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACvC,qCAAqC;IACrC,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/utils/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "thrivekit",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Tools to thrive with agentic coding - RALPH autonomous loop, Claude Code hooks, PRD-driven development",
|
|
5
|
+
"author": "Allie Jones <allie@allthrive.ai>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/allthriveai/thrivekit"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/allthriveai/thrivekit#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/allthriveai/thrivekit/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"agentic",
|
|
18
|
+
"claude",
|
|
19
|
+
"claude-code",
|
|
20
|
+
"autonomous",
|
|
21
|
+
"prd",
|
|
22
|
+
"ralph",
|
|
23
|
+
"pre-commit",
|
|
24
|
+
"code-quality"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "dist/index.js",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
|
+
"bin": {
|
|
30
|
+
"thrivekit": "./bin/thrivekit.sh",
|
|
31
|
+
"vibe-check": "./bin/vibe-check.js"
|
|
32
|
+
},
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"bin",
|
|
42
|
+
"ralph",
|
|
43
|
+
"templates",
|
|
44
|
+
".claude",
|
|
45
|
+
".pre-commit-hooks.yaml"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc",
|
|
49
|
+
"dev": "tsc --watch",
|
|
50
|
+
"test": "vitest",
|
|
51
|
+
"test:run": "vitest run",
|
|
52
|
+
"lint": "eslint src",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"clean": "rm -rf dist",
|
|
55
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
56
|
+
"postinstall": "./bin/postinstall.sh",
|
|
57
|
+
"release": "npm version patch && git push && git push --tags && npm publish",
|
|
58
|
+
"release:minor": "npm version minor && git push && git push --tags && npm publish",
|
|
59
|
+
"release:major": "npm version major && git push && git push --tags && npm publish"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18.0.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"playwright": ">=1.40.0"
|
|
66
|
+
},
|
|
67
|
+
"peerDependenciesMeta": {
|
|
68
|
+
"playwright": {
|
|
69
|
+
"optional": true
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"tsx": "^4.0.0"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@types/node": "^20.0.0",
|
|
77
|
+
"eslint": "^9.0.0",
|
|
78
|
+
"playwright": "^1.40.0",
|
|
79
|
+
"typescript": "^5.0.0",
|
|
80
|
+
"vitest": "^4.0.18"
|
|
81
|
+
}
|
|
82
|
+
}
|
package/ralph/api.sh
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shellcheck shell=bash
|
|
3
|
+
# api.sh - API validation for backend stories
|
|
4
|
+
|
|
5
|
+
# Parse an endpoint string into method and path
|
|
6
|
+
# Usage: parse_endpoint "POST /api/users" "http://localhost:3000"
|
|
7
|
+
# Sets: ENDPOINT_METHOD, ENDPOINT_PATH, ENDPOINT_URL
|
|
8
|
+
parse_endpoint() {
|
|
9
|
+
local endpoint="$1"
|
|
10
|
+
local base_url="${2:-}"
|
|
11
|
+
|
|
12
|
+
# Defaults
|
|
13
|
+
ENDPOINT_METHOD="GET"
|
|
14
|
+
ENDPOINT_PATH="$endpoint"
|
|
15
|
+
ENDPOINT_URL=""
|
|
16
|
+
|
|
17
|
+
# Parse method if present (e.g., "POST /api/contact")
|
|
18
|
+
if [[ "$endpoint" =~ ^(GET|POST|PUT|PATCH|DELETE)[[:space:]]+(.*) ]]; then
|
|
19
|
+
ENDPOINT_METHOD="${BASH_REMATCH[1]}"
|
|
20
|
+
ENDPOINT_PATH="${BASH_REMATCH[2]}"
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Build full URL
|
|
24
|
+
if [[ "$ENDPOINT_PATH" =~ ^https?:// ]]; then
|
|
25
|
+
ENDPOINT_URL="$ENDPOINT_PATH"
|
|
26
|
+
elif [[ -n "$base_url" ]]; then
|
|
27
|
+
ENDPOINT_URL="${base_url}${ENDPOINT_PATH}"
|
|
28
|
+
fi
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Check if endpoint is a WebSocket (can't be tested with HTTP)
|
|
32
|
+
is_websocket_endpoint() {
|
|
33
|
+
local endpoint="$1"
|
|
34
|
+
[[ "$endpoint" =~ ^wss?:// ]] || [[ "$endpoint" =~ ^(GET|POST|PUT|PATCH|DELETE)[[:space:]]+wss?:// ]]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Validate API endpoints for a backend story
|
|
38
|
+
run_api_validation() {
|
|
39
|
+
local story="$1"
|
|
40
|
+
|
|
41
|
+
# Get API endpoints from story
|
|
42
|
+
local endpoints
|
|
43
|
+
endpoints=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .apiEndpoints[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
44
|
+
|
|
45
|
+
if [[ -z "$endpoints" ]]; then
|
|
46
|
+
echo " (no apiEndpoints defined, skipping API validation)"
|
|
47
|
+
return 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Get base URL from config or use default
|
|
51
|
+
local base_url
|
|
52
|
+
base_url=$(get_config '.api.baseUrl' "http://localhost:3000")
|
|
53
|
+
|
|
54
|
+
local failed=0
|
|
55
|
+
|
|
56
|
+
echo " Validating API endpoints..."
|
|
57
|
+
|
|
58
|
+
while IFS= read -r endpoint; do
|
|
59
|
+
[[ -z "$endpoint" ]] && continue
|
|
60
|
+
|
|
61
|
+
# Skip WebSocket endpoints - they can't be tested with HTTP curl
|
|
62
|
+
if is_websocket_endpoint "$endpoint"; then
|
|
63
|
+
echo " Skipping WebSocket endpoint: $endpoint (use integration tests)"
|
|
64
|
+
continue
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Parse endpoint into method, path, and full URL
|
|
68
|
+
parse_endpoint "$endpoint" "$base_url"
|
|
69
|
+
|
|
70
|
+
echo -n " $ENDPOINT_METHOD $ENDPOINT_PATH... "
|
|
71
|
+
|
|
72
|
+
# Make the request
|
|
73
|
+
local response_code
|
|
74
|
+
response_code=$(curl -sf -m "$CURL_TIMEOUT_SECONDS" -o /dev/null -w "%{http_code}" -X "$ENDPOINT_METHOD" "$ENDPOINT_URL" 2>/dev/null)
|
|
75
|
+
|
|
76
|
+
if [[ "$response_code" =~ ^2[0-9][0-9]$ ]]; then
|
|
77
|
+
print_success "$response_code"
|
|
78
|
+
elif [[ "$response_code" == "000" ]]; then
|
|
79
|
+
print_error "connection failed"
|
|
80
|
+
failed=1
|
|
81
|
+
else
|
|
82
|
+
print_error "$response_code"
|
|
83
|
+
failed=1
|
|
84
|
+
fi
|
|
85
|
+
done <<< "$endpoints"
|
|
86
|
+
|
|
87
|
+
return $failed
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Run comprehensive API tests for a story
|
|
91
|
+
run_api_tests() {
|
|
92
|
+
local story="$1"
|
|
93
|
+
|
|
94
|
+
# Get test steps that look like API calls
|
|
95
|
+
local test_steps
|
|
96
|
+
test_steps=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .testSteps[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
97
|
+
|
|
98
|
+
if [[ -z "$test_steps" ]]; then
|
|
99
|
+
return 0
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
local failed=0
|
|
103
|
+
local log_file
|
|
104
|
+
log_file=$(create_temp_file ".log") || return 1
|
|
105
|
+
|
|
106
|
+
echo " Running API test steps..."
|
|
107
|
+
|
|
108
|
+
while IFS= read -r step; do
|
|
109
|
+
[[ -z "$step" ]] && continue
|
|
110
|
+
|
|
111
|
+
# Check if this looks like a curl command or API test
|
|
112
|
+
if [[ "$step" =~ ^curl ]]; then
|
|
113
|
+
echo -n " $step... "
|
|
114
|
+
|
|
115
|
+
if safe_exec "$step" "$log_file"; then
|
|
116
|
+
print_success "passed"
|
|
117
|
+
else
|
|
118
|
+
print_error "failed"
|
|
119
|
+
echo ""
|
|
120
|
+
echo " Response:"
|
|
121
|
+
tail -"$MAX_OUTPUT_PREVIEW_LINES" "$log_file" | sed 's/^/ /'
|
|
122
|
+
failed=1
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
done <<< "$test_steps"
|
|
126
|
+
|
|
127
|
+
rm -f "$log_file"
|
|
128
|
+
return $failed
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# Validate error handling for API
|
|
132
|
+
run_api_error_tests() {
|
|
133
|
+
local story="$1"
|
|
134
|
+
|
|
135
|
+
# Get error handling requirements
|
|
136
|
+
local error_handling
|
|
137
|
+
error_handling=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .errorHandling[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
138
|
+
|
|
139
|
+
if [[ -z "$error_handling" ]]; then
|
|
140
|
+
return 0
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# Get base URL and endpoints
|
|
144
|
+
local base_url
|
|
145
|
+
base_url=$(get_config '.api.baseUrl' "http://localhost:3000")
|
|
146
|
+
|
|
147
|
+
local endpoints
|
|
148
|
+
endpoints=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .apiEndpoints[0]?' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
149
|
+
|
|
150
|
+
if [[ -z "$endpoints" ]]; then
|
|
151
|
+
return 0
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# Skip WebSocket endpoints
|
|
155
|
+
if is_websocket_endpoint "$endpoints"; then
|
|
156
|
+
echo " Skipping error tests for WebSocket endpoint"
|
|
157
|
+
return 0
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# Parse endpoint (default to POST for error tests)
|
|
161
|
+
parse_endpoint "$endpoints" "$base_url"
|
|
162
|
+
[[ "$ENDPOINT_METHOD" == "GET" ]] && ENDPOINT_METHOD="POST"
|
|
163
|
+
|
|
164
|
+
local failed=0
|
|
165
|
+
|
|
166
|
+
echo " Testing API error handling..."
|
|
167
|
+
|
|
168
|
+
# Test common error cases
|
|
169
|
+
while IFS= read -r error_case; do
|
|
170
|
+
[[ -z "$error_case" ]] && continue
|
|
171
|
+
|
|
172
|
+
# Check for 400 tests (bad input)
|
|
173
|
+
if [[ "$error_case" =~ 400 ]]; then
|
|
174
|
+
echo -n " Testing 400 (bad request)... "
|
|
175
|
+
|
|
176
|
+
local response_code
|
|
177
|
+
response_code=$(curl -sf -m "$CURL_TIMEOUT_SECONDS" -o /dev/null -w "%{http_code}" \
|
|
178
|
+
-X "$ENDPOINT_METHOD" \
|
|
179
|
+
-H "Content-Type: application/json" \
|
|
180
|
+
-d '{}' \
|
|
181
|
+
"$ENDPOINT_URL" 2>/dev/null)
|
|
182
|
+
|
|
183
|
+
if [[ "$response_code" == "400" ]]; then
|
|
184
|
+
print_success "correctly returns 400"
|
|
185
|
+
else
|
|
186
|
+
print_warning "got $response_code (expected 400)"
|
|
187
|
+
failed=1
|
|
188
|
+
fi
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# Check for 401 tests (unauthorized)
|
|
192
|
+
if [[ "$error_case" =~ 401 ]]; then
|
|
193
|
+
echo -n " Testing 401 (unauthorized)... "
|
|
194
|
+
|
|
195
|
+
local response_code
|
|
196
|
+
response_code=$(curl -sf -m "$CURL_TIMEOUT_SECONDS" -o /dev/null -w "%{http_code}" \
|
|
197
|
+
-X "$ENDPOINT_METHOD" \
|
|
198
|
+
"$ENDPOINT_URL" 2>/dev/null)
|
|
199
|
+
|
|
200
|
+
if [[ "$response_code" == "401" ]]; then
|
|
201
|
+
print_success "correctly returns 401"
|
|
202
|
+
else
|
|
203
|
+
print_warning "got $response_code (expected 401)"
|
|
204
|
+
failed=1
|
|
205
|
+
fi
|
|
206
|
+
fi
|
|
207
|
+
done <<< "$error_handling"
|
|
208
|
+
|
|
209
|
+
return $failed
|
|
210
|
+
}
|