ownkey 1.0.0 → 1.0.2

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 (91) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +256 -81
  3. package/dist/ai/factory.js +2 -2
  4. package/dist/ai/factory.js.map +1 -1
  5. package/dist/ai/gemini.d.ts.map +1 -1
  6. package/dist/ai/gemini.js +13 -3
  7. package/dist/ai/gemini.js.map +1 -1
  8. package/dist/ai/ollama.d.ts +0 -2
  9. package/dist/ai/ollama.d.ts.map +1 -1
  10. package/dist/ai/ollama.js +20 -93
  11. package/dist/ai/ollama.js.map +1 -1
  12. package/dist/ai/openai.d.ts +4 -1
  13. package/dist/ai/openai.d.ts.map +1 -1
  14. package/dist/ai/openai.js +7 -3
  15. package/dist/ai/openai.js.map +1 -1
  16. package/dist/ai/provider.d.ts +4 -0
  17. package/dist/ai/provider.d.ts.map +1 -1
  18. package/dist/ai/provider.js +80 -47
  19. package/dist/ai/provider.js.map +1 -1
  20. package/dist/commands/apply.d.ts.map +1 -1
  21. package/dist/commands/apply.js +82 -47
  22. package/dist/commands/apply.js.map +1 -1
  23. package/dist/commands/config.d.ts.map +1 -1
  24. package/dist/commands/config.js +249 -101
  25. package/dist/commands/config.js.map +1 -1
  26. package/dist/commands/suggest.d.ts.map +1 -1
  27. package/dist/commands/suggest.js +44 -34
  28. package/dist/commands/suggest.js.map +1 -1
  29. package/dist/commands/undo.d.ts.map +1 -1
  30. package/dist/commands/undo.js +35 -25
  31. package/dist/commands/undo.js.map +1 -1
  32. package/dist/config/keychain.d.ts.map +1 -1
  33. package/dist/config/keychain.js +5 -1
  34. package/dist/config/keychain.js.map +1 -1
  35. package/dist/config/manager.d.ts +15 -2
  36. package/dist/config/manager.d.ts.map +1 -1
  37. package/dist/config/manager.js +42 -13
  38. package/dist/config/manager.js.map +1 -1
  39. package/dist/core/apply/engine.d.ts +19 -12
  40. package/dist/core/apply/engine.d.ts.map +1 -1
  41. package/dist/core/apply/engine.js +160 -149
  42. package/dist/core/apply/engine.js.map +1 -1
  43. package/dist/core/apply/index.d.ts +4 -2
  44. package/dist/core/apply/index.d.ts.map +1 -1
  45. package/dist/core/apply/index.js.map +1 -1
  46. package/dist/core/apply/interactive.d.ts +1 -1
  47. package/dist/core/apply/interactive.d.ts.map +1 -1
  48. package/dist/core/apply/interactive.js.map +1 -1
  49. package/dist/core/backup/index.d.ts +2 -1
  50. package/dist/core/backup/index.d.ts.map +1 -1
  51. package/dist/core/backup/index.js.map +1 -1
  52. package/dist/core/backup/manager.d.ts +6 -1
  53. package/dist/core/backup/manager.d.ts.map +1 -1
  54. package/dist/core/backup/manager.js +26 -18
  55. package/dist/core/backup/manager.js.map +1 -1
  56. package/dist/core/diff/applier.d.ts +23 -8
  57. package/dist/core/diff/applier.d.ts.map +1 -1
  58. package/dist/core/diff/applier.js +85 -80
  59. package/dist/core/diff/applier.js.map +1 -1
  60. package/dist/core/diff/index.d.ts +6 -3
  61. package/dist/core/diff/index.d.ts.map +1 -1
  62. package/dist/core/diff/index.js.map +1 -1
  63. package/dist/core/diff/parser.d.ts +2 -0
  64. package/dist/core/diff/parser.d.ts.map +1 -1
  65. package/dist/core/diff/parser.js +15 -5
  66. package/dist/core/diff/parser.js.map +1 -1
  67. package/dist/core/diff/validator.d.ts +3 -8
  68. package/dist/core/diff/validator.d.ts.map +1 -1
  69. package/dist/core/diff/validator.js +13 -71
  70. package/dist/core/diff/validator.js.map +1 -1
  71. package/dist/db/client.d.ts +9 -1
  72. package/dist/db/client.d.ts.map +1 -1
  73. package/dist/db/client.js +64 -86
  74. package/dist/db/client.js.map +1 -1
  75. package/dist/db/migrations/001_init.sql +80 -80
  76. package/dist/db/migrations/002_applies.sql +33 -32
  77. package/dist/index.js +13 -20
  78. package/dist/index.js.map +1 -1
  79. package/dist/storage/local.d.ts +12 -0
  80. package/dist/storage/local.d.ts.map +1 -1
  81. package/dist/storage/local.js +28 -0
  82. package/dist/storage/local.js.map +1 -1
  83. package/dist/types/index.d.ts +7 -0
  84. package/dist/types/index.d.ts.map +1 -1
  85. package/dist/types/index.js +1 -0
  86. package/dist/types/index.js.map +1 -1
  87. package/dist/utils/severity.d.ts +6 -0
  88. package/dist/utils/severity.d.ts.map +1 -0
  89. package/dist/utils/severity.js +19 -0
  90. package/dist/utils/severity.js.map +1 -0
  91. package/package.json +72 -72
package/dist/db/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import postgres from 'postgres';
2
2
  import { drizzle } from 'drizzle-orm/postgres-js';
3
- import { readFile } from 'fs/promises';
3
+ import { readFile, readdir } from 'fs/promises';
4
4
  import { join, dirname } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { DatabaseError } from '../types/index.js';
@@ -17,42 +17,48 @@ export class DatabaseClient {
17
17
  async connect(localOnly = false) {
18
18
  if (localOnly) {
19
19
  this.isLocalOnly = true;
20
- logger.info('Running in local-only mode (no database persistence)');
20
+ logger.verbose('Running in local-only mode (--local-only)');
21
21
  return;
22
22
  }
23
- try {
24
- const config = await configManager.load();
25
- if (!config.supabaseUrl || !config.supabaseServiceRoleKey) {
26
- logger.warn('Supabase not configured. Running in local-only mode.');
27
- logger.info('Run "ownkey config" to set up database persistence.');
28
- this.isLocalOnly = true;
29
- return;
23
+ const config = await configManager.load();
24
+ const url = configManager.getDatabaseUrl(config);
25
+ if (!url) {
26
+ this.isLocalOnly = true;
27
+ // Database is optional: stay silent unless an old, unusable config is present.
28
+ if (config.supabaseUrl) {
29
+ logger.warn('Database skipped: OwnKey needs your Postgres connection string, not the Supabase project URL.');
30
+ logger.info('Fix: ownkey config --database-url "postgresql://..." (Supabase → Connect)');
31
+ }
32
+ else {
33
+ logger.verbose('No database configured, using local storage');
30
34
  }
31
- // Extract database details from Supabase URL
32
- // Format: https://PROJECT_REF.supabase.co
33
- const supabaseUrl = new URL(config.supabaseUrl);
34
- const projectRef = supabaseUrl.hostname.split('.')[0];
35
- // Construct PostgreSQL connection string
36
- // Format: postgresql://postgres:PASSWORD@db.PROJECT_REF.supabase.co:5432/postgres
37
- const connectionString = `postgresql://postgres:${config.supabaseServiceRoleKey}@db.${projectRef}.supabase.co:5432/postgres`;
38
- this.sql = postgres(connectionString, {
39
- max: 10,
35
+ return;
36
+ }
37
+ try {
38
+ const isLocalHost = /@(localhost|127\.0\.0\.1)(:|\/)/.test(url);
39
+ this.sql = postgres(url, {
40
+ max: 1,
40
41
  idle_timeout: 20,
41
42
  connect_timeout: 10,
43
+ prepare: false, // required by Supabase's transaction pooler (port 6543)
44
+ ssl: isLocalHost || /sslmode=disable/.test(url) ? false : 'require',
45
+ onnotice: () => { },
42
46
  });
43
47
  this.db = drizzle(this.sql, { schema });
44
- // Test connection
45
48
  await this.sql `SELECT 1`;
46
49
  logger.verbose('Database connection established');
47
- // Run migrations
48
50
  await this.runMigrations();
49
51
  }
50
52
  catch (error) {
51
- logger.warn('Failed to connect to database. Running in local-only mode.');
52
- const errorMessage = error instanceof Error ? error.message : String(error);
53
- logger.error(`Database error details: ${errorMessage}`);
54
- if (error instanceof Error && error.stack) {
55
- logger.debug('Stack trace:', error.stack);
53
+ const e = error;
54
+ const message = e?.message || e?.code || e?.errors?.[0]?.message || e?.errors?.[0]?.code || String(error);
55
+ logger.warn(`Database unavailable (${message}). Using local storage.`);
56
+ logger.debug(`Database error: ${error instanceof Error ? error.stack : message}`);
57
+ try {
58
+ await this.sql?.end({ timeout: 1 });
59
+ }
60
+ catch {
61
+ // ignore
56
62
  }
57
63
  this.isLocalOnly = true;
58
64
  this.sql = null;
@@ -94,75 +100,47 @@ export class DatabaseClient {
94
100
  getClient() {
95
101
  return this.sql;
96
102
  }
103
+ /**
104
+ * Apply every migrations/NNN_name.sql that has not been recorded in schema_migrations.
105
+ * Each migration runs in its own transaction together with its version record.
106
+ */
97
107
  async runMigrations() {
98
108
  if (!this.sql) {
99
109
  return;
100
110
  }
101
- try {
102
- logger.verbose('Checking database migrations...');
103
- // Check if migrations table exists
104
- const tableCheck = await this.sql `
105
- SELECT EXISTS (
106
- SELECT FROM information_schema.tables
107
- WHERE table_schema = 'public'
108
- AND table_name = 'schema_migrations'
109
- ) as exists;
110
- `;
111
- const tableExists = tableCheck[0]?.exists === true;
112
- logger.debug(`schema_migrations table exists: ${tableExists}`);
113
- if (!tableExists) {
114
- logger.info('Initializing database schema...');
115
- await this.runMigration(1);
116
- logger.success('Database schema initialized');
117
- return;
118
- }
119
- // Check current version
120
- const versionCheck = await this.sql `
121
- SELECT MAX(version) as version FROM schema_migrations;
122
- `;
123
- const version = versionCheck[0]?.version || 0;
124
- logger.verbose(`Current schema version: ${version}`);
125
- // Run pending migrations
126
- const latestVersion = 1; // Update this as we add more migrations
127
- if (version < latestVersion) {
128
- for (let v = version + 1; v <= latestVersion; v++) {
129
- await this.runMigration(v);
130
- logger.success(`Applied migration ${v}`);
131
- }
132
- }
133
- else {
134
- logger.verbose('Database schema is up to date');
111
+ const sql = this.sql;
112
+ await sql `
113
+ CREATE TABLE IF NOT EXISTS schema_migrations (
114
+ version INTEGER PRIMARY KEY,
115
+ applied_at TIMESTAMP DEFAULT NOW() NOT NULL
116
+ )
117
+ `;
118
+ const rows = await sql `SELECT version FROM schema_migrations`;
119
+ const applied = new Set(rows.map((r) => Number(r.version)));
120
+ for (const migration of await DatabaseClient.listMigrations()) {
121
+ if (applied.has(migration.version))
122
+ continue;
123
+ logger.verbose(`Applying database migration ${migration.file}`);
124
+ const body = await readFile(migration.path, 'utf-8');
125
+ try {
126
+ await sql.begin(async (tx) => {
127
+ await tx.unsafe(body);
128
+ await tx.unsafe('INSERT INTO schema_migrations (version) VALUES ($1) ON CONFLICT (version) DO NOTHING', [migration.version]);
129
+ });
135
130
  }
136
- }
137
- catch (error) {
138
- const errorMsg = error instanceof Error ? error.message : String(error);
139
- logger.error(`Migration error: ${errorMsg}`);
140
- if (error instanceof Error && error.stack) {
141
- logger.debug(`Migration stack: ${error.stack}`);
131
+ catch (error) {
132
+ throw new DatabaseError(`Migration ${migration.file} failed: ${error.message}`, error);
142
133
  }
143
- throw new DatabaseError('Failed to run migrations', error);
144
134
  }
145
135
  }
146
- async runMigration(version) {
147
- if (!this.sql) {
148
- return;
149
- }
150
- const migrationFile = join(__dirname, 'migrations', `${String(version).padStart(3, '0')}_init.sql`);
151
- logger.verbose(`Running migration file: ${migrationFile}`);
152
- try {
153
- const migrationSql = await readFile(migrationFile, 'utf-8');
154
- logger.debug(`Migration SQL length: ${migrationSql.length} characters`);
155
- await this.sql.unsafe(migrationSql);
156
- logger.verbose(`Migration ${version} applied successfully`);
157
- }
158
- catch (error) {
159
- const errorMsg = error instanceof Error ? error.message : String(error);
160
- logger.error(`Migration ${version} failed: ${errorMsg}`);
161
- if (error instanceof Error && error.stack) {
162
- logger.debug(`Migration ${version} stack: ${error.stack}`);
163
- }
164
- throw new DatabaseError(`Failed to apply migration ${version}`, error);
165
- }
136
+ static async listMigrations() {
137
+ const dir = join(__dirname, 'migrations');
138
+ const files = await readdir(dir);
139
+ return files
140
+ .map((file) => ({ file, match: /^(\d+)_.+\.sql$/.exec(file) }))
141
+ .filter((f) => f.match)
142
+ .map((f) => ({ version: Number(f.match[1]), file: f.file, path: join(dir, f.file) }))
143
+ .sort((a, b) => a.version - b.version);
166
144
  }
167
145
  }
168
146
  export const dbClient = new DatabaseClient();
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"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"}
@@ -1,80 +1,80 @@
1
- -- OwnKey CLI Initial Schema Migration
2
- -- Version: 001
3
- -- Description: Create all initial tables for OwnKey CLI
4
-
5
- -- Projects table
6
- CREATE TABLE IF NOT EXISTS projects (
7
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
8
- name TEXT NOT NULL,
9
- root_path TEXT NOT NULL,
10
- created_at TIMESTAMP DEFAULT NOW() NOT NULL,
11
- updated_at TIMESTAMP DEFAULT NOW() NOT NULL
12
- );
13
-
14
- -- Runs table
15
- CREATE TABLE IF NOT EXISTS runs (
16
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
17
- project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
18
- command TEXT NOT NULL,
19
- status TEXT NOT NULL CHECK (status IN ('running', 'completed', 'failed')),
20
- summary TEXT,
21
- started_at TIMESTAMP DEFAULT NOW() NOT NULL,
22
- finished_at TIMESTAMP
23
- );
24
-
25
- -- Suggestions table
26
- CREATE TABLE IF NOT EXISTS suggestions (
27
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
28
- project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
29
- type TEXT NOT NULL CHECK (type IN ('bug', 'security', 'performance', 'refactor', 'style', 'architecture', 'test', 'documentation')),
30
- severity TEXT NOT NULL CHECK (severity IN ('critical', 'high', 'medium', 'low', 'info')),
31
- content TEXT NOT NULL,
32
- diff TEXT NOT NULL,
33
- file_path TEXT NOT NULL,
34
- rationale TEXT NOT NULL,
35
- tags JSONB,
36
- created_at TIMESTAMP DEFAULT NOW() NOT NULL
37
- );
38
-
39
- -- Suggestion history table
40
- CREATE TABLE IF NOT EXISTS suggestion_history (
41
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
42
- suggestion_id UUID NOT NULL REFERENCES suggestions(id) ON DELETE CASCADE,
43
- action TEXT NOT NULL CHECK (action IN ('applied', 'rejected', 'undone')),
44
- details TEXT,
45
- acted_at TIMESTAMP DEFAULT NOW() NOT NULL
46
- );
47
-
48
- -- Run suggestions junction table
49
- CREATE TABLE IF NOT EXISTS run_suggestions (
50
- run_id UUID NOT NULL REFERENCES runs(id) ON DELETE CASCADE,
51
- suggestion_id UUID NOT NULL REFERENCES suggestions(id) ON DELETE CASCADE,
52
- PRIMARY KEY (run_id, suggestion_id)
53
- );
54
-
55
- -- User API keys table (fingerprints only)
56
- CREATE TABLE IF NOT EXISTS user_api_keys (
57
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
58
- project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
59
- provider TEXT NOT NULL,
60
- key_fingerprint TEXT NOT NULL,
61
- added_at TIMESTAMP DEFAULT NOW() NOT NULL
62
- );
63
-
64
- -- Schema migrations table
65
- CREATE TABLE IF NOT EXISTS schema_migrations (
66
- version INTEGER PRIMARY KEY,
67
- applied_at TIMESTAMP DEFAULT NOW() NOT NULL
68
- );
69
-
70
- -- Create indexes for better query performance
71
- CREATE INDEX IF NOT EXISTS idx_runs_project_id ON runs(project_id);
72
- CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status);
73
- CREATE INDEX IF NOT EXISTS idx_suggestions_project_id ON suggestions(project_id);
74
- CREATE INDEX IF NOT EXISTS idx_suggestions_type ON suggestions(type);
75
- CREATE INDEX IF NOT EXISTS idx_suggestions_severity ON suggestions(severity);
76
- CREATE INDEX IF NOT EXISTS idx_suggestion_history_suggestion_id ON suggestion_history(suggestion_id);
77
- CREATE INDEX IF NOT EXISTS idx_user_api_keys_project_id ON user_api_keys(project_id);
78
-
79
- -- Insert migration record
80
- INSERT INTO schema_migrations (version) VALUES (1) ON CONFLICT (version) DO NOTHING;
1
+ -- OwnKey CLI Initial Schema Migration
2
+ -- Version: 001
3
+ -- Description: Create all initial tables for OwnKey CLI
4
+
5
+ -- Projects table
6
+ CREATE TABLE IF NOT EXISTS projects (
7
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
8
+ name TEXT NOT NULL,
9
+ root_path TEXT NOT NULL,
10
+ created_at TIMESTAMP DEFAULT NOW() NOT NULL,
11
+ updated_at TIMESTAMP DEFAULT NOW() NOT NULL
12
+ );
13
+
14
+ -- Runs table
15
+ CREATE TABLE IF NOT EXISTS runs (
16
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
17
+ project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
18
+ command TEXT NOT NULL,
19
+ status TEXT NOT NULL CHECK (status IN ('running', 'completed', 'failed')),
20
+ summary TEXT,
21
+ started_at TIMESTAMP DEFAULT NOW() NOT NULL,
22
+ finished_at TIMESTAMP
23
+ );
24
+
25
+ -- Suggestions table
26
+ CREATE TABLE IF NOT EXISTS suggestions (
27
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
28
+ project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
29
+ type TEXT NOT NULL CHECK (type IN ('bug', 'security', 'performance', 'refactor', 'style', 'architecture', 'test', 'documentation')),
30
+ severity TEXT NOT NULL CHECK (severity IN ('critical', 'high', 'medium', 'low', 'info')),
31
+ content TEXT NOT NULL,
32
+ diff TEXT NOT NULL,
33
+ file_path TEXT NOT NULL,
34
+ rationale TEXT NOT NULL,
35
+ tags JSONB,
36
+ created_at TIMESTAMP DEFAULT NOW() NOT NULL
37
+ );
38
+
39
+ -- Suggestion history table
40
+ CREATE TABLE IF NOT EXISTS suggestion_history (
41
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
42
+ suggestion_id UUID NOT NULL REFERENCES suggestions(id) ON DELETE CASCADE,
43
+ action TEXT NOT NULL CHECK (action IN ('applied', 'rejected', 'undone')),
44
+ details TEXT,
45
+ acted_at TIMESTAMP DEFAULT NOW() NOT NULL
46
+ );
47
+
48
+ -- Run suggestions junction table
49
+ CREATE TABLE IF NOT EXISTS run_suggestions (
50
+ run_id UUID NOT NULL REFERENCES runs(id) ON DELETE CASCADE,
51
+ suggestion_id UUID NOT NULL REFERENCES suggestions(id) ON DELETE CASCADE,
52
+ PRIMARY KEY (run_id, suggestion_id)
53
+ );
54
+
55
+ -- User API keys table (fingerprints only)
56
+ CREATE TABLE IF NOT EXISTS user_api_keys (
57
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
58
+ project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
59
+ provider TEXT NOT NULL,
60
+ key_fingerprint TEXT NOT NULL,
61
+ added_at TIMESTAMP DEFAULT NOW() NOT NULL
62
+ );
63
+
64
+ -- Schema migrations table
65
+ CREATE TABLE IF NOT EXISTS schema_migrations (
66
+ version INTEGER PRIMARY KEY,
67
+ applied_at TIMESTAMP DEFAULT NOW() NOT NULL
68
+ );
69
+
70
+ -- Create indexes for better query performance
71
+ CREATE INDEX IF NOT EXISTS idx_runs_project_id ON runs(project_id);
72
+ CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status);
73
+ CREATE INDEX IF NOT EXISTS idx_suggestions_project_id ON suggestions(project_id);
74
+ CREATE INDEX IF NOT EXISTS idx_suggestions_type ON suggestions(type);
75
+ CREATE INDEX IF NOT EXISTS idx_suggestions_severity ON suggestions(severity);
76
+ CREATE INDEX IF NOT EXISTS idx_suggestion_history_suggestion_id ON suggestion_history(suggestion_id);
77
+ CREATE INDEX IF NOT EXISTS idx_user_api_keys_project_id ON user_api_keys(project_id);
78
+
79
+ -- Insert migration record
80
+ INSERT INTO schema_migrations (version) VALUES (1) ON CONFLICT (version) DO NOTHING;
@@ -1,32 +1,33 @@
1
- -- Migration: Add applies table for tracking applied suggestions
2
- -- Version: 002
3
-
4
- CREATE TABLE IF NOT EXISTS applies (
5
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
6
- suggestion_id UUID NOT NULL,
7
- files_modified JSONB NOT NULL,
8
- backup_id TEXT,
9
- applied_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
10
- status TEXT NOT NULL CHECK (status IN ('success', 'failed', 'rolled_back')),
11
- error TEXT,
12
- metadata JSONB,
13
- created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
14
- );
15
-
16
- -- Index for faster lookups
17
- CREATE INDEX IF NOT EXISTS idx_applies_suggestion_id ON applies(suggestion_id);
18
- CREATE INDEX IF NOT EXISTS idx_applies_applied_at ON applies(applied_at DESC);
19
- CREATE INDEX IF NOT EXISTS idx_applies_status ON applies(status);
20
-
21
- -- Add foreign key if suggestions table exists
22
- -- Note: This will fail gracefully if suggestions table doesn't exist yet
23
- DO $$
24
- BEGIN
25
- IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'suggestions') THEN
26
- ALTER TABLE applies
27
- ADD CONSTRAINT fk_applies_suggestion
28
- FOREIGN KEY (suggestion_id)
29
- REFERENCES suggestions(id)
30
- ON DELETE CASCADE;
31
- END IF;
32
- END $$;
1
+ -- Migration: Add applies table for tracking applied suggestions
2
+ -- Version: 002
3
+
4
+ CREATE TABLE IF NOT EXISTS applies (
5
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
6
+ suggestion_id UUID NOT NULL,
7
+ files_modified JSONB NOT NULL,
8
+ backup_id TEXT,
9
+ applied_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
10
+ status TEXT NOT NULL CHECK (status IN ('success', 'failed', 'rolled_back')),
11
+ error TEXT,
12
+ metadata JSONB,
13
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
14
+ );
15
+
16
+ -- Index for faster lookups
17
+ CREATE INDEX IF NOT EXISTS idx_applies_suggestion_id ON applies(suggestion_id);
18
+ CREATE INDEX IF NOT EXISTS idx_applies_applied_at ON applies(applied_at DESC);
19
+ CREATE INDEX IF NOT EXISTS idx_applies_status ON applies(status);
20
+
21
+ -- Add foreign key if suggestions table exists
22
+ -- Note: This will fail gracefully if suggestions table doesn't exist yet
23
+ DO $$
24
+ BEGIN
25
+ IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'suggestions')
26
+ AND NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_applies_suggestion') THEN
27
+ ALTER TABLE applies
28
+ ADD CONSTRAINT fk_applies_suggestion
29
+ FOREIGN KEY (suggestion_id)
30
+ REFERENCES suggestions(id)
31
+ ON DELETE CASCADE;
32
+ END IF;
33
+ END $$;
package/dist/index.js CHANGED
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
3
  import { logger } from './utils/logger.js';
4
- import { configManager } from './config/manager.js';
5
4
  import { dbClient } from './db/client.js';
6
- import chalk from 'chalk';
5
+ import { createRequire } from 'module';
7
6
  // Import commands
8
7
  import { configCommand } from './commands/config.js';
9
8
  import { scanCommand } from './commands/scan.js';
@@ -14,7 +13,7 @@ const program = new Command();
14
13
  program
15
14
  .name('ownkey')
16
15
  .description('Local-first AI coding agent for intelligent codebase analysis')
17
- .version('1.0.0');
16
+ .version(createRequire(import.meta.url)('../package.json').version);
18
17
  // Global options
19
18
  program.option('--local-only', 'Run without database persistence');
20
19
  program.option('--verbose', 'Enable verbose logging');
@@ -26,11 +25,13 @@ program.addCommand(scanCommand);
26
25
  program.addCommand(suggestCommand);
27
26
  program.addCommand(applyCommand);
28
27
  program.addCommand(undoCommand);
29
- // Pre-parse hook to set up logging and check config
30
- program.hook('preAction', async (thisCommand) => {
28
+ // Pre-action hook: set up logging and storage.
29
+ // Note: the hook is registered on the root program, so `thisCommand` is always the
30
+ // root. The command actually being run is the second argument (`actionCommand`).
31
+ program.hook('preAction', async (thisCommand, actionCommand) => {
31
32
  await logger.init();
33
+ // Global flags live on the root program
32
34
  const opts = thisCommand.opts();
33
- // Set log level
34
35
  if (opts.debug) {
35
36
  logger.setLevel('debug');
36
37
  }
@@ -40,25 +41,17 @@ program.hook('preAction', async (thisCommand) => {
40
41
  else if (opts.quiet) {
41
42
  logger.setLevel('quiet');
42
43
  }
43
- // Check if this is the config command
44
- const isConfigCommand = thisCommand.name() === 'config';
45
- // For non-config commands, check if config exists
46
- if (!isConfigCommand) {
47
- const configExists = await configManager.exists();
48
- if (!configExists) {
49
- console.log(chalk.yellow('\n⚠️ No configuration found.\n'));
50
- console.log('OwnKey needs to be configured before use.');
51
- console.log(chalk.cyan('\nRun: ownkey config\n'));
52
- process.exit(1);
53
- }
54
- // Connect to database (or enter local-only mode)
55
- await dbClient.connect(opts.localOnly);
44
+ // `config` must always run, even with no configuration yet; it never needs the database.
45
+ if (actionCommand.name() === 'config') {
46
+ return;
56
47
  }
48
+ // Optional database (Supabase/Postgres). Silent no-op when not configured.
49
+ await dbClient.connect(Boolean(opts.localOnly));
57
50
  });
58
51
  // Post-action hook to clean up
59
52
  program.hook('postAction', async () => {
60
53
  await dbClient.disconnect();
61
54
  });
62
55
  // Parse arguments
63
- program.parse();
56
+ await program.parseAsync();
64
57
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEvC,kBAAkB;AAClB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACF,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,+DAA+D,CAAC;KAC5E,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC;AAExE,iBAAiB;AACjB,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,kCAAkC,CAAC,CAAC;AACnE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAC;AACtD,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;AAClD,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,+BAA+B,CAAC,CAAC;AAE3D,WAAW;AACX,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAEhC,+CAA+C;AAC/C,mFAAmF;AACnF,iFAAiF;AACjF,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,EAAE;IAC3D,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IAEpB,wCAAwC;IACxC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IAEhC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;SAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACtB,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;SAAM,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACpB,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,yFAAyF;IACzF,IAAI,aAAa,CAAC,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO;IACX,CAAC;IAED,2EAA2E;IAC3E,MAAM,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,+BAA+B;AAC/B,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;IAClC,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC;AAChC,CAAC,CAAC,CAAC;AAEH,kBAAkB;AAClB,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC"}
@@ -4,6 +4,10 @@ export interface StoredSuggestion extends Suggestion {
4
4
  projectPath: string;
5
5
  projectName: string;
6
6
  createdAt: string;
7
+ /** Matching row id in the optional database */
8
+ dbId?: string;
9
+ appliedAt?: string;
10
+ backupId?: string;
7
11
  }
8
12
  export interface BackupInfo {
9
13
  id: string;
@@ -37,6 +41,14 @@ export declare class LocalStorage {
37
41
  * Load a single suggestion by ID
38
42
  */
39
43
  loadSuggestion(id: string): Promise<StoredSuggestion | null>;
44
+ /**
45
+ * Merge fields into a stored suggestion (e.g. mark applied, link a database id)
46
+ */
47
+ updateSuggestion(id: string, patch: Partial<StoredSuggestion>): Promise<StoredSuggestion | null>;
48
+ /**
49
+ * After an undo: mark the suggestions applied with this backup as pending again
50
+ */
51
+ clearApplied(backupId: string): Promise<void>;
40
52
  /**
41
53
  * Load all suggestions for a specific project
42
54
  */
@@ -1 +1 @@
1
- {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG/C,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAS;;IAS3B;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB3B;;;OAGG;IACG,eAAe,CACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,UAAU,EAAE,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAgD9B;;OAEG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAKlE;;OAEG;IACG,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAKjF;;OAEG;YACW,kBAAkB;IAUhC;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAgBnF;;OAEG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAaxD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;QACzB,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;KACnC,CAAC;CAaL;AAGD,eAAO,MAAM,YAAY,cAAqB,CAAC"}
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG/C,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAS;;IAS3B;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB3B;;;OAGG;IACG,eAAe,CACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,UAAU,EAAE,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAgD9B;;OAEG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAKlE;;OAEG;IACG,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAStG;;OAEG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAanD;;OAEG;IACG,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAKjF;;OAEG;YACW,kBAAkB;IAUhC;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAgBnF;;OAEG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAaxD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;QACzB,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;KACnC,CAAC;CAaL;AAGD,eAAO,MAAM,YAAY,cAAqB,CAAC"}
@@ -91,6 +91,34 @@ export class LocalStorage {
91
91
  const all = await this.loadAllSuggestions();
92
92
  return all.find(s => s.id === id) || null;
93
93
  }
94
+ /**
95
+ * Merge fields into a stored suggestion (e.g. mark applied, link a database id)
96
+ */
97
+ async updateSuggestion(id, patch) {
98
+ const all = await this.loadAllSuggestions();
99
+ const index = all.findIndex((s) => s.id === id);
100
+ if (index === -1)
101
+ return null;
102
+ all[index] = { ...all[index], ...patch, id };
103
+ await writeFile(this.suggestionsFile, JSON.stringify(all, null, 2));
104
+ return all[index];
105
+ }
106
+ /**
107
+ * After an undo: mark the suggestions applied with this backup as pending again
108
+ */
109
+ async clearApplied(backupId) {
110
+ const all = await this.loadAllSuggestions();
111
+ let changed = false;
112
+ for (const s of all) {
113
+ if (s.backupId === backupId) {
114
+ delete s.appliedAt;
115
+ delete s.backupId;
116
+ changed = true;
117
+ }
118
+ }
119
+ if (changed)
120
+ await writeFile(this.suggestionsFile, JSON.stringify(all, null, 2));
121
+ }
94
122
  /**
95
123
  * Load all suggestions for a specific project
96
124
  */