@ariacode/cli 0.1.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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +404 -0
  3. package/dist/actions.d.ts +271 -0
  4. package/dist/actions.js +1809 -0
  5. package/dist/actions.js.map +1 -0
  6. package/dist/agent.d.ts +26 -0
  7. package/dist/agent.js +182 -0
  8. package/dist/agent.js.map +1 -0
  9. package/dist/app.d.ts +14 -0
  10. package/dist/app.js +83 -0
  11. package/dist/app.js.map +1 -0
  12. package/dist/cli.d.ts +12 -0
  13. package/dist/cli.js +157 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config.d.ts +170 -0
  16. package/dist/config.js +291 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/context.d.ts +58 -0
  19. package/dist/context.js +44 -0
  20. package/dist/context.js.map +1 -0
  21. package/dist/parser.d.ts +39 -0
  22. package/dist/parser.js +323 -0
  23. package/dist/parser.js.map +1 -0
  24. package/dist/prompts/ask.md +20 -0
  25. package/dist/prompts/explore.md +38 -0
  26. package/dist/prompts/patch.md +27 -0
  27. package/dist/prompts/plan.md +41 -0
  28. package/dist/prompts/prompts/ask.md +20 -0
  29. package/dist/prompts/prompts/explore.md +38 -0
  30. package/dist/prompts/prompts/patch.md +27 -0
  31. package/dist/prompts/prompts/plan.md +41 -0
  32. package/dist/prompts/prompts/review.md +33 -0
  33. package/dist/prompts/review.md +33 -0
  34. package/dist/provider.d.ts +148 -0
  35. package/dist/provider.js +486 -0
  36. package/dist/provider.js.map +1 -0
  37. package/dist/repo.d.ts +22 -0
  38. package/dist/repo.js +154 -0
  39. package/dist/repo.js.map +1 -0
  40. package/dist/safety.d.ts +48 -0
  41. package/dist/safety.js +140 -0
  42. package/dist/safety.js.map +1 -0
  43. package/dist/storage.d.ts +133 -0
  44. package/dist/storage.js +300 -0
  45. package/dist/storage.js.map +1 -0
  46. package/dist/tools.d.ts +70 -0
  47. package/dist/tools.js +654 -0
  48. package/dist/tools.js.map +1 -0
  49. package/dist/ui.d.ts +203 -0
  50. package/dist/ui.js +410 -0
  51. package/dist/ui.js.map +1 -0
  52. package/package.json +73 -0
package/dist/repo.js ADDED
@@ -0,0 +1,154 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ /**
4
+ * Detect project type based on framework indicators
5
+ * Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9
6
+ */
7
+ export function detectProjectType(rootDir = process.cwd()) {
8
+ const rootPath = resolve(rootDir);
9
+ const packageJsonPath = join(rootPath, 'package.json');
10
+ // Check if package.json exists
11
+ if (!existsSync(packageJsonPath)) {
12
+ throw new Error('No package.json found in project root');
13
+ }
14
+ // Parse package.json
15
+ let packageJson = null;
16
+ try {
17
+ packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
18
+ }
19
+ catch (error) {
20
+ throw new Error(`Failed to parse package.json: ${error}`);
21
+ }
22
+ const deps = {
23
+ ...packageJson?.dependencies,
24
+ ...packageJson?.devDependencies,
25
+ };
26
+ // Detect Next.js (Requirement 4.1)
27
+ const hasNextDep = !!deps?.next;
28
+ const hasNextConfig = existsSync(join(rootPath, 'next.config.js')) ||
29
+ existsSync(join(rootPath, 'next.config.mjs')) ||
30
+ existsSync(join(rootPath, 'next.config.ts')) ||
31
+ existsSync(join(rootPath, 'next.config.cjs'));
32
+ // Detect Prisma once for all project types
33
+ const prisma = detectPrisma(rootPath, deps);
34
+ const packageManager = detectPackageManager(rootPath);
35
+ if (hasNextDep || hasNextConfig) {
36
+ // Detect Next.js router type (Requirements 4.2, 4.3)
37
+ const router = detectNextJsRouter(rootPath);
38
+ return {
39
+ type: "nextjs",
40
+ framework: {
41
+ name: "Next.js",
42
+ version: deps?.next,
43
+ router,
44
+ },
45
+ hasPrisma: prisma.hasPrisma,
46
+ prismaSchemaPath: prisma.prismaSchemaPath,
47
+ packageJsonPath,
48
+ packageManager,
49
+ rootPath,
50
+ };
51
+ }
52
+ // Detect Nest.js (Requirement 4.4)
53
+ const hasNestDep = !!deps?.['@nestjs/core'];
54
+ const hasNestCli = existsSync(join(rootPath, 'nest-cli.json'));
55
+ if (hasNestDep || hasNestCli) {
56
+ return {
57
+ type: "nestjs",
58
+ framework: {
59
+ name: "Nest.js",
60
+ version: deps?.['@nestjs/core'],
61
+ },
62
+ hasPrisma: prisma.hasPrisma,
63
+ prismaSchemaPath: prisma.prismaSchemaPath,
64
+ packageJsonPath,
65
+ packageManager,
66
+ rootPath,
67
+ };
68
+ }
69
+ // Fallback to Node.js (Requirement 4.5)
70
+ return {
71
+ type: "nodejs",
72
+ hasPrisma: prisma.hasPrisma,
73
+ prismaSchemaPath: prisma.prismaSchemaPath,
74
+ packageJsonPath,
75
+ packageManager,
76
+ rootPath,
77
+ };
78
+ }
79
+ /**
80
+ * Detect Next.js router type (app vs pages)
81
+ * Requirements: 4.2, 4.3
82
+ */
83
+ function detectNextJsRouter(rootPath) {
84
+ const appDir = join(rootPath, 'app');
85
+ const pagesDir = join(rootPath, 'pages');
86
+ // Check for app router indicators (Requirement 4.2)
87
+ if (existsSync(appDir)) {
88
+ try {
89
+ const appContents = readdirSync(appDir);
90
+ const hasAppRouter = appContents.some(file => file.startsWith('layout.') || file.startsWith('page.'));
91
+ if (hasAppRouter) {
92
+ return "app";
93
+ }
94
+ }
95
+ catch {
96
+ // Ignore read errors
97
+ }
98
+ }
99
+ // Check for pages router indicators (Requirement 4.3)
100
+ if (existsSync(pagesDir)) {
101
+ try {
102
+ const pagesContents = readdirSync(pagesDir);
103
+ const hasPagesRouter = pagesContents.some(file => file.startsWith('_app.') || file.startsWith('_document.'));
104
+ if (hasPagesRouter) {
105
+ return "pages";
106
+ }
107
+ }
108
+ catch {
109
+ // Ignore read errors
110
+ }
111
+ }
112
+ // If directories exist but no clear indicators, prefer app router
113
+ if (existsSync(appDir))
114
+ return "app";
115
+ if (existsSync(pagesDir))
116
+ return "pages";
117
+ return undefined;
118
+ }
119
+ /**
120
+ * Detect Prisma presence and schema path
121
+ * Requirement: 4.6
122
+ */
123
+ function detectPrisma(rootPath, deps) {
124
+ const hasPrismaDep = !!deps?.prisma || !!deps?.['@prisma/client'];
125
+ const prismaSchemaPath = join(rootPath, 'prisma', 'schema.prisma');
126
+ const hasPrismaSchema = existsSync(prismaSchemaPath);
127
+ if (hasPrismaDep || hasPrismaSchema) {
128
+ return {
129
+ hasPrisma: true,
130
+ prismaSchemaPath: hasPrismaSchema ? prismaSchemaPath : undefined,
131
+ };
132
+ }
133
+ return { hasPrisma: false };
134
+ }
135
+ /**
136
+ * Detect package manager from lockfile presence
137
+ * Requirement: 4.7
138
+ */
139
+ function detectPackageManager(rootPath) {
140
+ if (existsSync(join(rootPath, 'pnpm-lock.yaml'))) {
141
+ return "pnpm";
142
+ }
143
+ if (existsSync(join(rootPath, 'yarn.lock'))) {
144
+ return "yarn";
145
+ }
146
+ if (existsSync(join(rootPath, 'bun.lockb'))) {
147
+ return "bun";
148
+ }
149
+ if (existsSync(join(rootPath, 'package-lock.json'))) {
150
+ return "npm";
151
+ }
152
+ return undefined;
153
+ }
154
+ //# sourceMappingURL=repo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.js","sourceRoot":"","sources":["../src/repo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Safety error for boundary violations and resource limits
3
+ */
4
+ export declare class SafetyError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ /**
8
+ * Validate that a file path is within the project root boundary
9
+ * Prevents path traversal attacks and symlink escapes
10
+ *
11
+ * @param inputPath - Path to validate (can be relative or absolute)
12
+ * @param projectRoot - Absolute path to project root
13
+ * @throws SafetyError if path is outside project root
14
+ *
15
+ * Requirements: 7.1, 7.3, 7.4, 8.7, 23.4
16
+ */
17
+ export declare function validatePath(inputPath: string, projectRoot: string): void;
18
+ /**
19
+ * Validate that a file size is within the configured limit
20
+ *
21
+ * @param filePath - Path to the file to check
22
+ * @param maxFileSizeKb - Maximum file size in kilobytes
23
+ * @throws SafetyError if file exceeds size limit
24
+ *
25
+ * Requirements: 7.7
26
+ */
27
+ export declare function validateFileSize(filePath: string, maxFileSizeKb: number): void;
28
+ /**
29
+ * Validate that a patch size (number of files) is within the configured limit
30
+ *
31
+ * @param fileCount - Number of files in the patch
32
+ * @param maxFilesPerPatch - Maximum number of files allowed per patch
33
+ * @throws SafetyError if patch exceeds file count limit
34
+ *
35
+ * Requirements: 7.8
36
+ */
37
+ export declare function validatePatchSize(fileCount: number, maxFilesPerPatch: number): void;
38
+ /**
39
+ * Validate that a shell command is in the allowlist
40
+ * Only the binary name is checked, not arguments
41
+ *
42
+ * @param command - Shell command to validate
43
+ * @param allowedCommands - List of allowed binary names
44
+ * @throws SafetyError if command is not in allowlist
45
+ *
46
+ * Requirements: 7.9, 23.5
47
+ */
48
+ export declare function validateShellCommand(command: string, allowedCommands: string[]): void;
package/dist/safety.js ADDED
@@ -0,0 +1,140 @@
1
+ import * as nodePath from "node:path";
2
+ import * as fs from "node:fs";
3
+ /**
4
+ * Safety error for boundary violations and resource limits
5
+ */
6
+ export class SafetyError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "SafetyError";
10
+ }
11
+ }
12
+ /**
13
+ * Validate that a file path is within the project root boundary
14
+ * Prevents path traversal attacks and symlink escapes
15
+ *
16
+ * @param inputPath - Path to validate (can be relative or absolute)
17
+ * @param projectRoot - Absolute path to project root
18
+ * @throws SafetyError if path is outside project root
19
+ *
20
+ * Requirements: 7.1, 7.3, 7.4, 8.7, 23.4
21
+ */
22
+ export function validatePath(inputPath, projectRoot) {
23
+ // Resolve the input path relative to project root
24
+ const resolved = nodePath.resolve(projectRoot, inputPath);
25
+ // Check for path traversal using separator to prevent prefix bugs
26
+ // Example: /project should not validate for /projects/other
27
+ const isExactMatch = resolved === projectRoot;
28
+ const isWithinProject = resolved.startsWith(projectRoot + nodePath.sep);
29
+ if (!isExactMatch && !isWithinProject) {
30
+ throw new SafetyError(`Path outside project root: ${inputPath} resolves to ${resolved}`);
31
+ }
32
+ // Check for symlink escape
33
+ // For write operations, the file might not exist yet (ENOENT is acceptable)
34
+ try {
35
+ const real = fs.realpathSync(resolved);
36
+ const isRealExactMatch = real === projectRoot;
37
+ const isRealWithinProject = real.startsWith(projectRoot + nodePath.sep);
38
+ if (!isRealExactMatch && !isRealWithinProject) {
39
+ throw new SafetyError(`Symlink escape detected: ${inputPath} resolves to ${real} outside project root`);
40
+ }
41
+ }
42
+ catch (error) {
43
+ // Handle ENOENT for write operations (file doesn't exist yet)
44
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
45
+ // File doesn't exist yet - this is acceptable for write operations
46
+ // The resolved path check above is sufficient
47
+ return;
48
+ }
49
+ // Re-throw other errors
50
+ throw error;
51
+ }
52
+ }
53
+ /**
54
+ * Validate that a file size is within the configured limit
55
+ *
56
+ * @param filePath - Path to the file to check
57
+ * @param maxFileSizeKb - Maximum file size in kilobytes
58
+ * @throws SafetyError if file exceeds size limit
59
+ *
60
+ * Requirements: 7.7
61
+ */
62
+ export function validateFileSize(filePath, maxFileSizeKb) {
63
+ try {
64
+ const stats = fs.statSync(filePath);
65
+ const fileSizeKb = stats.size / 1024;
66
+ if (fileSizeKb > maxFileSizeKb) {
67
+ throw new SafetyError(`File size exceeds limit: ${filePath} is ${fileSizeKb.toFixed(2)}KB, max is ${maxFileSizeKb}KB`);
68
+ }
69
+ }
70
+ catch (error) {
71
+ if (error instanceof SafetyError) {
72
+ throw error;
73
+ }
74
+ // If file doesn't exist or can't be read, let the caller handle it
75
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
76
+ throw new SafetyError(`File not found: ${filePath}`);
77
+ }
78
+ throw error;
79
+ }
80
+ }
81
+ /**
82
+ * Validate that a patch size (number of files) is within the configured limit
83
+ *
84
+ * @param fileCount - Number of files in the patch
85
+ * @param maxFilesPerPatch - Maximum number of files allowed per patch
86
+ * @throws SafetyError if patch exceeds file count limit
87
+ *
88
+ * Requirements: 7.8
89
+ */
90
+ export function validatePatchSize(fileCount, maxFilesPerPatch) {
91
+ if (fileCount > maxFilesPerPatch) {
92
+ throw new SafetyError(`Patch size exceeds limit: ${fileCount} files, max is ${maxFilesPerPatch}`);
93
+ }
94
+ }
95
+ /**
96
+ * Extract the binary name from a shell command string
97
+ * Handles quoted commands and arguments
98
+ *
99
+ * @param command - Shell command string
100
+ * @returns Binary name (first token)
101
+ */
102
+ function extractBinary(command) {
103
+ const trimmed = command.trim();
104
+ // Handle quoted commands
105
+ if (trimmed.startsWith('"')) {
106
+ const endQuote = trimmed.indexOf('"', 1);
107
+ if (endQuote !== -1) {
108
+ return trimmed.substring(1, endQuote);
109
+ }
110
+ }
111
+ if (trimmed.startsWith("'")) {
112
+ const endQuote = trimmed.indexOf("'", 1);
113
+ if (endQuote !== -1) {
114
+ return trimmed.substring(1, endQuote);
115
+ }
116
+ }
117
+ // Extract first token (split on whitespace)
118
+ const tokens = trimmed.split(/\s+/);
119
+ return tokens[0] || "";
120
+ }
121
+ /**
122
+ * Validate that a shell command is in the allowlist
123
+ * Only the binary name is checked, not arguments
124
+ *
125
+ * @param command - Shell command to validate
126
+ * @param allowedCommands - List of allowed binary names
127
+ * @throws SafetyError if command is not in allowlist
128
+ *
129
+ * Requirements: 7.9, 23.5
130
+ */
131
+ export function validateShellCommand(command, allowedCommands) {
132
+ const binary = extractBinary(command);
133
+ if (!binary) {
134
+ throw new SafetyError("Empty shell command");
135
+ }
136
+ if (!allowedCommands.includes(binary)) {
137
+ throw new SafetyError(`Shell command not allowed: ${binary}. Allowed commands: ${allowedCommands.join(", ")}`);
138
+ }
139
+ }
140
+ //# sourceMappingURL=safety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safety.js","sourceRoot":"","sources":["../src/safety.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ import Database from "better-sqlite3";
2
+ import { z } from "zod";
3
+ export declare const SessionStatusSchema: z.ZodEnum<{
4
+ running: "running";
5
+ completed: "completed";
6
+ failed: "failed";
7
+ cancelled: "cancelled";
8
+ }>;
9
+ export type SessionStatus = z.infer<typeof SessionStatusSchema>;
10
+ export declare const MessageRoleSchema: z.ZodEnum<{
11
+ user: "user";
12
+ assistant: "assistant";
13
+ system: "system";
14
+ }>;
15
+ export type MessageRole = z.infer<typeof MessageRoleSchema>;
16
+ export declare const RiskLevelSchema: z.ZodEnum<{
17
+ low: "low";
18
+ medium: "medium";
19
+ high: "high";
20
+ }>;
21
+ export type RiskLevel = z.infer<typeof RiskLevelSchema>;
22
+ export interface Session {
23
+ id: string;
24
+ command: string;
25
+ projectRoot: string;
26
+ provider: string;
27
+ model: string;
28
+ status: SessionStatus;
29
+ createdAt: string;
30
+ completedAt: string | null;
31
+ error: string | null;
32
+ }
33
+ export interface Message {
34
+ id: number;
35
+ sessionId: string;
36
+ role: MessageRole;
37
+ content: string;
38
+ createdAt: string;
39
+ }
40
+ export interface ToolExecution {
41
+ id: number;
42
+ sessionId: string;
43
+ toolName: string;
44
+ input: string;
45
+ output: string | null;
46
+ error: string | null;
47
+ createdAt: string;
48
+ }
49
+ export interface Mutation {
50
+ id: number;
51
+ sessionId: string;
52
+ action: string;
53
+ affectedFiles: string;
54
+ riskLevel: RiskLevel;
55
+ reversible: boolean;
56
+ rollbackHints: string | null;
57
+ createdAt: string;
58
+ }
59
+ /**
60
+ * Get or create database connection to ~/.aria/history.db
61
+ * Sets file permissions to 600 (user-only read/write)
62
+ */
63
+ export declare function getDatabase(): Database.Database;
64
+ /**
65
+ * Close database connection
66
+ */
67
+ export declare function closeDatabase(): void;
68
+ /**
69
+ * Get current schema version from database
70
+ * Returns 0 if schema_versions table doesn't exist
71
+ */
72
+ export declare function getCurrentSchemaVersion(db: Database.Database): number;
73
+ /**
74
+ * Run database migrations sequentially
75
+ */
76
+ export declare function runMigrations(db: Database.Database): void;
77
+ /**
78
+ * Initialize database with schema
79
+ */
80
+ export declare function initializeDatabase(): Database.Database;
81
+ /**
82
+ * Create a new session
83
+ */
84
+ export declare function createSession(db: Database.Database, session: {
85
+ id: string;
86
+ command: string;
87
+ projectRoot: string;
88
+ provider: string;
89
+ model: string;
90
+ }): void;
91
+ /**
92
+ * Update session status
93
+ */
94
+ export declare function updateSessionStatus(db: Database.Database, sessionId: string, status: SessionStatus, error?: string): void;
95
+ /**
96
+ * Get a single session by ID
97
+ */
98
+ export declare function getSession(db: Database.Database, sessionId: string): Session | null;
99
+ /**
100
+ * List sessions with pagination
101
+ */
102
+ export declare function listSessions(db: Database.Database, options?: {
103
+ limit?: number;
104
+ offset?: number;
105
+ status?: SessionStatus;
106
+ }): Session[];
107
+ /**
108
+ * Log a message to the database
109
+ */
110
+ export declare function logMessage(db: Database.Database, sessionId: string, role: MessageRole, content: string): void;
111
+ /**
112
+ * Log a tool execution to the database
113
+ */
114
+ export declare function logToolExecution(db: Database.Database, sessionId: string, toolName: string, input: unknown, result: {
115
+ success: boolean;
116
+ data?: unknown;
117
+ error?: string;
118
+ }): void;
119
+ /**
120
+ * Log a mutation to the database
121
+ */
122
+ export declare function logMutation(db: Database.Database, sessionId: string, mutation: {
123
+ action: string;
124
+ affectedFiles: string[];
125
+ riskLevel: RiskLevel;
126
+ reversible: boolean;
127
+ rollbackHints?: string[];
128
+ }): void;
129
+ /**
130
+ * Delete sessions older than retainDays
131
+ * Cascading deletes will remove associated messages, tool_executions, and mutations
132
+ */
133
+ export declare function deleteOldSessions(db: Database.Database, retainDays: number): number;