decisionnode 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/dist/setup.js ADDED
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ // Load environment variables first
3
+ import './env.js';
4
+ import fs from 'fs/promises';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import readline from 'readline';
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ /**
11
+ * Prompt user for input
12
+ */
13
+ function prompt(question) {
14
+ const rl = readline.createInterface({
15
+ input: process.stdin,
16
+ output: process.stdout
17
+ });
18
+ return new Promise((resolve) => {
19
+ rl.question(question, (answer) => {
20
+ rl.close();
21
+ resolve(answer.trim());
22
+ });
23
+ });
24
+ }
25
+ /**
26
+ * Get the MCP config file path
27
+ */
28
+ function getMCPConfigPath() {
29
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
30
+ return path.join(homeDir, '.gemini', 'antigravity', 'mcp_config.json');
31
+ }
32
+ /**
33
+ * Read existing MCP config
34
+ */
35
+ async function readMCPConfig(configPath) {
36
+ try {
37
+ const content = await fs.readFile(configPath, 'utf-8');
38
+ return JSON.parse(content);
39
+ }
40
+ catch (error) {
41
+ // File doesn't exist or is invalid, return empty config
42
+ return { mcpServers: {} };
43
+ }
44
+ }
45
+ /**
46
+ * Write MCP config
47
+ */
48
+ async function writeMCPConfig(configPath, config) {
49
+ // Ensure directory exists
50
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
51
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
52
+ }
53
+ /**
54
+ * Try to read API key from environment (already loaded by env.ts)
55
+ */
56
+ function getApiKeyFromEnv() {
57
+ return process.env.GEMINI_API_KEY || null;
58
+ }
59
+ /**
60
+ * Main setup flow
61
+ */
62
+ export async function runSetup() {
63
+ console.log('🧠 DecisionNode MCP Setup\n');
64
+ // 1. Try to auto-detect API key from .env
65
+ let apiKey = getApiKeyFromEnv();
66
+ if (apiKey) {
67
+ console.log('āœ“ Found GEMINI_API_KEY in .env file\n');
68
+ }
69
+ else {
70
+ console.log('Step 1: Gemini API Key');
71
+ console.log('Get your free API key from: https://aistudio.google.com/api-keys\n');
72
+ apiKey = await prompt('Enter your GEMINI_API_KEY (or press Enter to skip): ');
73
+ if (!apiKey) {
74
+ console.log('\nāš ļø Skipping API key for now. You can add it later in the MCP config file.');
75
+ }
76
+ }
77
+ // 2. Get MCP config path
78
+ const configPath = getMCPConfigPath();
79
+ console.log(`\nStep 2: Updating MCP config at:\n${configPath}\n`);
80
+ // 3. Read existing config
81
+ let config;
82
+ try {
83
+ config = await readMCPConfig(configPath);
84
+ console.log('āœ“ Found existing MCP config');
85
+ }
86
+ catch (error) {
87
+ config = { mcpServers: {} };
88
+ console.log('āœ“ Creating new MCP config');
89
+ }
90
+ // 4. Add/update DecisionNode entry for THIS project
91
+ if (!config.mcpServers) {
92
+ config.mcpServers = {};
93
+ }
94
+ // Single entry - Antigravity sets cwd based on active workspace
95
+ config.mcpServers.decisionnode = {
96
+ command: 'npx',
97
+ args: ['decide-mcp'],
98
+ ...(apiKey ? { env: { GEMINI_API_KEY: apiKey } } : {})
99
+ };
100
+ // 5. Write config
101
+ try {
102
+ await writeMCPConfig(configPath, config);
103
+ console.log('āœ“ Updated MCP config\n');
104
+ }
105
+ catch (error) {
106
+ console.error('āœ— Failed to write MCP config:', error);
107
+ process.exit(1);
108
+ }
109
+ // 6. Success message
110
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
111
+ const globalStore = path.join(homeDir, '.decisionnode');
112
+ console.log('āœ… DecisionNode MCP setup complete!\n');
113
+ console.log('šŸ“ Global store: ' + globalStore);
114
+ console.log(' Each project gets its own isolated folder automatically.\n');
115
+ if (!apiKey) {
116
+ console.log('šŸ“ To add your API key later, edit:');
117
+ console.log(` ${configPath}\n`);
118
+ console.log(' Add this to the "decisionnode" entry:');
119
+ console.log(' "env": { "GEMINI_API_KEY": "your-key-here" }\n');
120
+ }
121
+ console.log('šŸš€ Next steps:');
122
+ console.log(' 1. Restart Antigravity (or reload MCP servers)');
123
+ console.log(' 2. Open any project - decisions are automatically isolated!');
124
+ console.log(' 3. Try asking: "Add a decision about our architecture"\n');
125
+ }
126
+ // Run setup if called directly
127
+ if (import.meta.url === `file://${process.argv[1]}`) {
128
+ runSetup().catch((error) => {
129
+ console.error('Setup failed:', error);
130
+ process.exit(1);
131
+ });
132
+ }
@@ -0,0 +1,126 @@
1
+ import { DecisionNode, DecisionCollection } from './types.js';
2
+ import { SourceType } from './history.js';
3
+ /**
4
+ * Get all available scopes by scanning the .decisions directory
5
+ */
6
+ export declare function getAvailableScopes(): Promise<string[]>;
7
+ /**
8
+ * Get all available scopes in the global decisions folder
9
+ */
10
+ export declare function getGlobalScopes(): Promise<string[]>;
11
+ /**
12
+ * Load all decisions for a given scope from the global folder
13
+ */
14
+ export declare function loadGlobalDecisions(scope: string): Promise<DecisionCollection>;
15
+ /**
16
+ * Save decisions for a given scope in the global folder
17
+ */
18
+ export declare function saveGlobalDecisions(collection: DecisionCollection): Promise<void>;
19
+ /**
20
+ * List all global decisions, optionally filtered by scope
21
+ * Returns decisions with "global:" prefix on IDs
22
+ */
23
+ export declare function listGlobalDecisions(scope?: string): Promise<DecisionNode[]>;
24
+ /**
25
+ * Get a global decision by ID (with or without "global:" prefix)
26
+ */
27
+ export declare function getGlobalDecisionById(id: string): Promise<DecisionNode | null>;
28
+ /**
29
+ * Generate the next decision ID for a global scope
30
+ * Returns ID without the "global:" prefix (prefix is added on read)
31
+ */
32
+ export declare function getNextGlobalDecisionId(scope: string): Promise<string>;
33
+ /**
34
+ * Add a new global decision
35
+ */
36
+ export declare function addGlobalDecision(decision: DecisionNode, source?: SourceType): Promise<{
37
+ embedded: boolean;
38
+ }>;
39
+ /**
40
+ * Update an existing global decision
41
+ */
42
+ export declare function updateGlobalDecision(id: string, updates: Partial<DecisionNode>): Promise<DecisionNode | null>;
43
+ /**
44
+ * Delete a global decision
45
+ */
46
+ export declare function deleteGlobalDecision(id: string): Promise<boolean>;
47
+ /**
48
+ * Normalize scope to consistent capitalized format
49
+ * e.g., 'ui', 'UI', 'Ui', 'uI' all become 'UI'
50
+ * e.g., 'backend', 'Backend', 'BACKEND' all become 'Backend'
51
+ */
52
+ export declare function normalizeScope(scope: string): string;
53
+ export interface ProjectInfo {
54
+ name: string;
55
+ decisionCount: number;
56
+ scopes: string[];
57
+ lastModified?: string;
58
+ }
59
+ /**
60
+ * List all available projects in the global store
61
+ * Returns project name, decision count, and available scopes
62
+ */
63
+ export declare function listProjects(): Promise<ProjectInfo[]>;
64
+ /**
65
+ * Load all decisions for a given scope
66
+ */
67
+ export declare function loadDecisions(scope: string): Promise<DecisionCollection>;
68
+ /**
69
+ * Save decisions for a given scope
70
+ */
71
+ export declare function saveDecisions(collection: DecisionCollection): Promise<void>;
72
+ /**
73
+ * Get a single decision by ID across all scopes
74
+ */
75
+ export declare function getDecisionById(id: string): Promise<DecisionNode | null>;
76
+ /**
77
+ * List all decisions, optionally filtered by scope
78
+ */
79
+ export declare function listDecisions(scope?: string): Promise<DecisionNode[]>;
80
+ /**
81
+ * Add a new decision
82
+ * Auto-embeds and logs the action
83
+ * @param decision - The decision to add
84
+ * @param source - Where this action originated (default: 'cli')
85
+ */
86
+ export declare function addDecision(decision: DecisionNode, source?: SourceType): Promise<{
87
+ embedded: boolean;
88
+ }>;
89
+ /**
90
+ * Update an existing decision
91
+ * Auto-embeds and logs the action
92
+ */
93
+ export declare function updateDecision(id: string, updates: Partial<DecisionNode>): Promise<DecisionNode | null>;
94
+ /**
95
+ * Delete a decision by ID
96
+ * Clears embedding and logs the action
97
+ */
98
+ export declare function deleteDecision(id: string): Promise<boolean>;
99
+ /**
100
+ * Delete an entire scope (all decisions within it)
101
+ * Deletes the scope file, embeddings, and optionally from cloud
102
+ */
103
+ export declare function deleteScope(scope: string): Promise<{
104
+ deleted: number;
105
+ decisionIds: string[];
106
+ }>;
107
+ /**
108
+ * Generate the next decision ID for a scope
109
+ */
110
+ export declare function getNextDecisionId(scope: string): Promise<string>;
111
+ /**
112
+ * Renumber decisions in a scope after deletion
113
+ * Also updates embeddings for renamed IDs
114
+ */
115
+ export declare function renumberDecisions(scope: string): Promise<string[]>;
116
+ /**
117
+ * Import decisions from a JSON file or object
118
+ * Auto-embeds all imported decisions
119
+ */
120
+ export declare function importDecisions(decisions: DecisionNode[], options?: {
121
+ overwrite?: boolean;
122
+ }): Promise<{
123
+ added: number;
124
+ skipped: number;
125
+ embedded: number;
126
+ }>;