gencode-ai 0.1.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/agent/agent.d.ts +44 -2
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +130 -11
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/types.d.ts +11 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/checkpointing/checkpoint-manager.d.ts +87 -0
- package/dist/checkpointing/checkpoint-manager.d.ts.map +1 -0
- package/dist/checkpointing/checkpoint-manager.js +281 -0
- package/dist/checkpointing/checkpoint-manager.js.map +1 -0
- package/dist/checkpointing/index.d.ts +29 -0
- package/dist/checkpointing/index.d.ts.map +1 -0
- package/dist/checkpointing/index.js +29 -0
- package/dist/checkpointing/index.js.map +1 -0
- package/dist/checkpointing/types.d.ts +98 -0
- package/dist/checkpointing/types.d.ts.map +1 -0
- package/dist/checkpointing/types.js +7 -0
- package/dist/checkpointing/types.js.map +1 -0
- package/dist/cli/components/App.d.ts.map +1 -1
- package/dist/cli/components/App.js +171 -14
- package/dist/cli/components/App.js.map +1 -1
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
- package/dist/cli/components/CommandSuggestions.js +5 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -1
- package/dist/cli/components/Messages.d.ts +7 -1
- package/dist/cli/components/Messages.d.ts.map +1 -1
- package/dist/cli/components/Messages.js +12 -3
- package/dist/cli/components/Messages.js.map +1 -1
- package/dist/cli/components/ModeIndicator.d.ts +42 -0
- package/dist/cli/components/ModeIndicator.d.ts.map +1 -0
- package/dist/cli/components/ModeIndicator.js +52 -0
- package/dist/cli/components/ModeIndicator.js.map +1 -0
- package/dist/cli/components/ModelSelector.d.ts +4 -3
- package/dist/cli/components/ModelSelector.d.ts.map +1 -1
- package/dist/cli/components/ModelSelector.js +54 -37
- package/dist/cli/components/ModelSelector.js.map +1 -1
- package/dist/cli/components/PlanApproval.d.ts +36 -0
- package/dist/cli/components/PlanApproval.d.ts.map +1 -0
- package/dist/cli/components/PlanApproval.js +154 -0
- package/dist/cli/components/PlanApproval.js.map +1 -0
- package/dist/cli/components/ProviderManager.d.ts +2 -2
- package/dist/cli/components/ProviderManager.d.ts.map +1 -1
- package/dist/cli/components/ProviderManager.js +137 -156
- package/dist/cli/components/ProviderManager.js.map +1 -1
- package/dist/cli/components/theme.d.ts +2 -0
- package/dist/cli/components/theme.d.ts.map +1 -1
- package/dist/cli/components/theme.js +3 -0
- package/dist/cli/components/theme.js.map +1 -1
- package/dist/cli/index.js +30 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/levels.d.ts +5 -5
- package/dist/config/levels.d.ts.map +1 -1
- package/dist/config/levels.js +20 -20
- package/dist/config/levels.js.map +1 -1
- package/dist/config/merger.js +1 -1
- package/dist/config/merger.js.map +1 -1
- package/dist/config/providers-config.d.ts +8 -5
- package/dist/config/providers-config.d.ts.map +1 -1
- package/dist/config/providers-config.js +19 -22
- package/dist/config/providers-config.js.map +1 -1
- package/dist/config/test-utils.d.ts +2 -2
- package/dist/config/test-utils.d.ts.map +1 -1
- package/dist/config/test-utils.js +4 -4
- package/dist/config/test-utils.js.map +1 -1
- package/dist/config/types.d.ts +23 -17
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +14 -14
- package/dist/config/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/memory-manager.d.ts +25 -12
- package/dist/memory/memory-manager.d.ts.map +1 -1
- package/dist/memory/memory-manager.js +241 -112
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/test-utils.d.ts +1 -1
- package/dist/memory/test-utils.d.ts.map +1 -1
- package/dist/memory/test-utils.js +3 -3
- package/dist/memory/test-utils.js.map +1 -1
- package/dist/memory/types.d.ts +20 -10
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +13 -13
- package/dist/memory/types.js.map +1 -1
- package/dist/migration/migrate.d.ts +24 -0
- package/dist/migration/migrate.d.ts.map +1 -0
- package/dist/migration/migrate.js +164 -0
- package/dist/migration/migrate.js.map +1 -0
- package/dist/permissions/persistence.d.ts +2 -2
- package/dist/permissions/persistence.js +4 -4
- package/dist/permissions/persistence.js.map +1 -1
- package/dist/planning/index.d.ts +13 -0
- package/dist/planning/index.d.ts.map +1 -0
- package/dist/planning/index.js +15 -0
- package/dist/planning/index.js.map +1 -0
- package/dist/planning/plan-file.d.ts +59 -0
- package/dist/planning/plan-file.d.ts.map +1 -0
- package/dist/planning/plan-file.js +278 -0
- package/dist/planning/plan-file.js.map +1 -0
- package/dist/planning/state.d.ts +127 -0
- package/dist/planning/state.d.ts.map +1 -0
- package/dist/planning/state.js +261 -0
- package/dist/planning/state.js.map +1 -0
- package/dist/planning/tools/enter-plan-mode.d.ts +25 -0
- package/dist/planning/tools/enter-plan-mode.d.ts.map +1 -0
- package/dist/planning/tools/enter-plan-mode.js +98 -0
- package/dist/planning/tools/enter-plan-mode.js.map +1 -0
- package/dist/planning/tools/exit-plan-mode.d.ts +24 -0
- package/dist/planning/tools/exit-plan-mode.d.ts.map +1 -0
- package/dist/planning/tools/exit-plan-mode.js +149 -0
- package/dist/planning/tools/exit-plan-mode.js.map +1 -0
- package/dist/planning/types.d.ts +100 -0
- package/dist/planning/types.d.ts.map +1 -0
- package/dist/planning/types.js +28 -0
- package/dist/planning/types.js.map +1 -0
- package/dist/pricing/calculator.d.ts +21 -0
- package/dist/pricing/calculator.d.ts.map +1 -0
- package/dist/pricing/calculator.js +59 -0
- package/dist/pricing/calculator.js.map +1 -0
- package/dist/pricing/index.d.ts +7 -0
- package/dist/pricing/index.d.ts.map +1 -0
- package/dist/pricing/index.js +7 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/pricing/models.d.ts +20 -0
- package/dist/pricing/models.d.ts.map +1 -0
- package/dist/pricing/models.js +322 -0
- package/dist/pricing/models.js.map +1 -0
- package/dist/pricing/types.d.ts +30 -0
- package/dist/pricing/types.d.ts.map +1 -0
- package/dist/pricing/types.js +5 -0
- package/dist/pricing/types.js.map +1 -0
- package/dist/prompts/index.d.ts +5 -4
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +11 -8
- package/dist/prompts/index.js.map +1 -1
- package/dist/providers/anthropic.d.ts +2 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +24 -10
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/gemini.d.ts +2 -1
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +28 -14
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/index.d.ts +20 -10
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +48 -24
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts +2 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +19 -8
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/registry.d.ts +48 -34
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +72 -88
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/store.d.ts +43 -17
- package/dist/providers/store.d.ts.map +1 -1
- package/dist/providers/store.js +112 -19
- package/dist/providers/store.js.map +1 -1
- package/dist/providers/types.d.ts +25 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/vertex-ai.d.ts +15 -7
- package/dist/providers/vertex-ai.d.ts.map +1 -1
- package/dist/providers/vertex-ai.js +63 -23
- package/dist/providers/vertex-ai.js.map +1 -1
- package/dist/session/manager.d.ts +4 -0
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js +8 -0
- package/dist/session/manager.js.map +1 -1
- package/dist/session/types.js +1 -1
- package/dist/session/types.js.map +1 -1
- package/dist/tools/index.d.ts +7 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/registry.d.ts +13 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +79 -2
- package/dist/tools/registry.js.map +1 -1
- package/docs/config-system-comparison.md +50 -50
- package/docs/cost-tracking-comparison.md +904 -0
- package/docs/memory-system.md +124 -31
- package/docs/operating-modes.md +96 -0
- package/docs/permissions.md +2 -2
- package/docs/proposals/0006-memory-system.md +4 -4
- package/docs/proposals/0008-checkpointing.md +109 -2
- package/docs/proposals/0011-custom-commands.md +2 -1
- package/docs/proposals/0021-skills-system.md +2 -1
- package/docs/proposals/0023-permission-enhancements.md +2 -2
- package/docs/proposals/0025-cost-tracking.md +60 -2
- package/docs/proposals/0033-enterprise-deployment.md +1 -1
- package/docs/proposals/0041-configuration-system.md +17 -19
- package/docs/proposals/0042-prompt-optimization.md +17 -9
- package/docs/proposals/README.md +6 -6
- package/docs/providers.md +94 -9
- package/examples/test-checkpointing.ts +121 -0
- package/examples/test-cost-tracking.ts +77 -0
- package/examples/test-interrupt-cleanup.ts +94 -0
- package/package.json +3 -2
- package/scripts/migrate.ts +449 -0
- package/src/agent/agent.ts +161 -12
- package/src/agent/types.ts +11 -1
- package/src/checkpointing/checkpoint-manager.ts +327 -0
- package/src/checkpointing/index.ts +45 -0
- package/src/checkpointing/types.ts +104 -0
- package/src/cli/components/App.tsx +221 -13
- package/src/cli/components/CommandSuggestions.tsx +5 -0
- package/src/cli/components/Messages.tsx +24 -5
- package/src/cli/components/ModeIndicator.tsx +174 -0
- package/src/cli/components/ModelSelector.tsx +62 -43
- package/src/cli/components/PlanApproval.tsx +327 -0
- package/src/cli/components/ProviderManager.tsx +278 -323
- package/src/cli/components/theme.ts +3 -0
- package/src/cli/index.tsx +36 -17
- package/src/config/index.ts +5 -3
- package/src/config/levels.test.ts +22 -22
- package/src/config/levels.ts +22 -22
- package/src/config/loader.test.ts +14 -14
- package/src/config/manager.test.ts +19 -19
- package/src/config/merger.test.ts +23 -23
- package/src/config/merger.ts +1 -1
- package/src/config/providers-config.ts +23 -21
- package/src/config/test-utils.ts +6 -6
- package/src/config/types.ts +30 -20
- package/src/index.ts +15 -0
- package/src/memory/memory-manager.test.ts +242 -24
- package/src/memory/memory-manager.ts +270 -141
- package/src/memory/test-utils.ts +4 -4
- package/src/memory/types.ts +28 -17
- package/src/permissions/persistence.ts +4 -4
- package/src/planning/index.ts +53 -0
- package/src/planning/plan-file.ts +326 -0
- package/src/planning/state.ts +305 -0
- package/src/planning/tools/enter-plan-mode.ts +111 -0
- package/src/planning/tools/exit-plan-mode.ts +170 -0
- package/src/planning/types.ts +150 -0
- package/src/pricing/calculator.ts +71 -0
- package/src/pricing/index.ts +7 -0
- package/src/pricing/models.ts +334 -0
- package/src/pricing/types.ts +32 -0
- package/src/prompts/index.ts +13 -9
- package/src/providers/anthropic.ts +30 -10
- package/src/providers/gemini.ts +34 -14
- package/src/providers/index.ts +76 -33
- package/src/providers/openai.ts +26 -8
- package/src/providers/registry.ts +116 -111
- package/src/providers/store.ts +130 -28
- package/src/providers/types.ts +36 -1
- package/src/providers/vertex-ai.ts +70 -23
- package/src/session/manager.ts +9 -0
- package/src/session/types.ts +1 -1
- package/src/tools/index.ts +8 -0
- package/src/tools/registry.ts +95 -2
- package/.gencode/settings.local.json +0 -7
- package/CLAUDE.md +0 -86
package/src/memory/types.ts
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* Memory System Types
|
|
3
3
|
*
|
|
4
4
|
* Hierarchical memory loading compatible with Claude Code:
|
|
5
|
-
* At each level, both .
|
|
5
|
+
* At each level, both .gen and .claude are loaded and merged (gen has higher priority).
|
|
6
6
|
*
|
|
7
7
|
* Levels:
|
|
8
8
|
* - Enterprise: System-wide managed memory (enforced)
|
|
9
|
-
* - User: ~/.
|
|
10
|
-
* - User Rules: ~/.
|
|
11
|
-
* - Extra:
|
|
12
|
-
* - Project: ./
|
|
13
|
-
* - Project Rules: .
|
|
9
|
+
* - User: ~/.gen/ + ~/.claude/ (both loaded, gen content appears later)
|
|
10
|
+
* - User Rules: ~/.gen/rules/ + ~/.claude/rules/
|
|
11
|
+
* - Extra: GEN_CONFIG_DIRS directories
|
|
12
|
+
* - Project: ./GEN.md, .gen/, ./CLAUDE.md, .claude/ (recursive upward search)
|
|
13
|
+
* - Project Rules: .gen/rules/ + .claude/rules/
|
|
14
14
|
* - Local: *.local.md files (gitignored)
|
|
15
15
|
*/
|
|
16
16
|
|
|
@@ -23,7 +23,16 @@ export type MemoryLevel =
|
|
|
23
23
|
| 'project-rules'
|
|
24
24
|
| 'local';
|
|
25
25
|
|
|
26
|
-
export type MemoryNamespace = '
|
|
26
|
+
export type MemoryNamespace = 'gen' | 'claude' | 'extra';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Memory merge strategy - how to combine CLAUDE.md and GEN.md at each level
|
|
30
|
+
* - fallback: Load GEN.md if exists, else CLAUDE.md (reduces context, default)
|
|
31
|
+
* - both: Load both CLAUDE.md and GEN.md (current behavior, max context)
|
|
32
|
+
* - gen-only: Only load .gen/GEN.md files
|
|
33
|
+
* - claude-only: Only load .claude/CLAUDE.md files
|
|
34
|
+
*/
|
|
35
|
+
export type MemoryMergeStrategy = 'fallback' | 'both' | 'gen-only' | 'claude-only';
|
|
27
36
|
|
|
28
37
|
export interface MemoryFile {
|
|
29
38
|
path: string;
|
|
@@ -46,9 +55,9 @@ export interface MemoryRule {
|
|
|
46
55
|
|
|
47
56
|
export interface MemoryConfig {
|
|
48
57
|
// GenCode file names (higher priority)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
genFilename: string;
|
|
59
|
+
genLocalFilename: string;
|
|
60
|
+
genDir: string;
|
|
52
61
|
|
|
53
62
|
// Claude file names (lower priority, loaded first)
|
|
54
63
|
claudeFilename: string;
|
|
@@ -64,9 +73,9 @@ export interface MemoryConfig {
|
|
|
64
73
|
|
|
65
74
|
export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
|
|
66
75
|
// GenCode
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
genFilename: 'GEN.md',
|
|
77
|
+
genLocalFilename: 'GEN.local.md',
|
|
78
|
+
genDir: '.gen',
|
|
70
79
|
|
|
71
80
|
// Claude
|
|
72
81
|
claudeFilename: 'CLAUDE.md',
|
|
@@ -82,13 +91,13 @@ export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
|
|
|
82
91
|
|
|
83
92
|
// Legacy compatibility
|
|
84
93
|
export const LEGACY_MEMORY_CONFIG = {
|
|
85
|
-
primaryFilename: '
|
|
94
|
+
primaryFilename: 'GEN.md',
|
|
86
95
|
fallbackFilename: 'CLAUDE.md',
|
|
87
|
-
localFilename: '
|
|
96
|
+
localFilename: 'GEN.local.md',
|
|
88
97
|
localFallbackFilename: 'CLAUDE.local.md',
|
|
89
|
-
primaryUserDir: '.
|
|
98
|
+
primaryUserDir: '.gen',
|
|
90
99
|
fallbackUserDir: '.claude',
|
|
91
|
-
primaryLocalDir: '.
|
|
100
|
+
primaryLocalDir: '.gen',
|
|
92
101
|
fallbackLocalDir: '.claude',
|
|
93
102
|
rulesDir: 'rules',
|
|
94
103
|
maxFileSize: 100 * 1024,
|
|
@@ -103,6 +112,7 @@ export interface LoadedMemory {
|
|
|
103
112
|
context: string;
|
|
104
113
|
errors: string[];
|
|
105
114
|
sources: MemorySource[]; // For debugging
|
|
115
|
+
skippedFiles: string[]; // Files skipped due to merge strategy
|
|
106
116
|
}
|
|
107
117
|
|
|
108
118
|
export interface MemorySource {
|
|
@@ -116,4 +126,5 @@ export interface MemorySource {
|
|
|
116
126
|
export interface MemoryLoadOptions {
|
|
117
127
|
cwd: string;
|
|
118
128
|
currentFile?: string; // For activating path-scoped rules
|
|
129
|
+
strategy?: MemoryMergeStrategy; // How to merge CLAUDE.md and AGENT.md (default: 'fallback')
|
|
119
130
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Permission Persistence - Store and load permission rules
|
|
3
3
|
*
|
|
4
4
|
* Handles persistent storage of permission rules at:
|
|
5
|
-
* - Global: ~/.
|
|
6
|
-
* - Project: .
|
|
5
|
+
* - Global: ~/.gen/permissions.json
|
|
6
|
+
* - Project: .gen/permissions.json
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import * as fs from 'fs/promises';
|
|
@@ -21,7 +21,7 @@ import { parsePatternString } from './prompt-matcher.js';
|
|
|
21
21
|
|
|
22
22
|
const PERMISSIONS_VERSION = 1;
|
|
23
23
|
const PERMISSIONS_FILE = 'permissions.json';
|
|
24
|
-
const GLOBAL_DIR = path.join(os.homedir(), '.
|
|
24
|
+
const GLOBAL_DIR = path.join(os.homedir(), '.gen');
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Permission Persistence Manager
|
|
@@ -32,7 +32,7 @@ export class PermissionPersistence {
|
|
|
32
32
|
|
|
33
33
|
constructor(projectPath?: string) {
|
|
34
34
|
this.globalDir = GLOBAL_DIR;
|
|
35
|
-
this.projectDir = projectPath ? path.join(projectPath, '.
|
|
35
|
+
this.projectDir = projectPath ? path.join(projectPath, '.gen') : null;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planning Module
|
|
3
|
+
*
|
|
4
|
+
* Plan Mode for GenCode - allows the LLM to design implementation
|
|
5
|
+
* approaches before writing code, with read-only exploration tools.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Types
|
|
9
|
+
export type {
|
|
10
|
+
PlanPhase,
|
|
11
|
+
PlanApprovalOption,
|
|
12
|
+
AllowedPrompt,
|
|
13
|
+
PlanModeState,
|
|
14
|
+
PlanFile,
|
|
15
|
+
PlanModeAllowedTool,
|
|
16
|
+
PlanModeBlockedTool,
|
|
17
|
+
ModeType,
|
|
18
|
+
PlanApprovalState,
|
|
19
|
+
PlanModeEvent,
|
|
20
|
+
} from './types.js';
|
|
21
|
+
|
|
22
|
+
export { PLAN_MODE_ALLOWED_TOOLS, PLAN_MODE_BLOCKED_TOOLS } from './types.js';
|
|
23
|
+
|
|
24
|
+
// State Management
|
|
25
|
+
export {
|
|
26
|
+
PlanModeManager,
|
|
27
|
+
getPlanModeManager,
|
|
28
|
+
resetPlanModeManager,
|
|
29
|
+
isPlanModeActive,
|
|
30
|
+
getCurrentMode,
|
|
31
|
+
enterPlanMode,
|
|
32
|
+
exitPlanMode,
|
|
33
|
+
togglePlanMode,
|
|
34
|
+
} from './state.js';
|
|
35
|
+
|
|
36
|
+
// Plan File Utilities
|
|
37
|
+
export {
|
|
38
|
+
generatePlanFileName,
|
|
39
|
+
getPlansDir,
|
|
40
|
+
ensurePlansDir,
|
|
41
|
+
createPlanFile,
|
|
42
|
+
readPlanFile,
|
|
43
|
+
writePlanFile,
|
|
44
|
+
listPlanFiles,
|
|
45
|
+
deletePlanFile,
|
|
46
|
+
parseFilesToChange,
|
|
47
|
+
parsePreApprovedPermissions,
|
|
48
|
+
getDisplayPath,
|
|
49
|
+
} from './plan-file.js';
|
|
50
|
+
|
|
51
|
+
// Tools
|
|
52
|
+
export { enterPlanModeTool } from './tools/enter-plan-mode.js';
|
|
53
|
+
export { exitPlanModeTool } from './tools/exit-plan-mode.js';
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan File Utilities
|
|
3
|
+
*
|
|
4
|
+
* Manages plan files stored in .gen/plans/ directory.
|
|
5
|
+
* Generates unique filenames with timestamps and slugs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { existsSync } from 'fs';
|
|
11
|
+
import type { PlanFile } from './types.js';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Constants
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
const PLANS_DIR = '.gen/plans';
|
|
18
|
+
const PLAN_FILE_EXTENSION = '.md';
|
|
19
|
+
|
|
20
|
+
// Word lists for generating memorable names (like Claude Code)
|
|
21
|
+
const ADJECTIVES = [
|
|
22
|
+
'agile', 'bold', 'calm', 'deft', 'eager', 'fair', 'glad', 'humble',
|
|
23
|
+
'ideal', 'jolly', 'keen', 'lively', 'merry', 'noble', 'polite', 'quiet',
|
|
24
|
+
'rapid', 'smart', 'tidy', 'unique', 'vivid', 'warm', 'zealous', 'bright',
|
|
25
|
+
'clear', 'crisp', 'fresh', 'golden', 'happy', 'lovely', 'neat', 'proud',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const NOUNS = [
|
|
29
|
+
'alpine', 'beacon', 'cipher', 'delta', 'ember', 'falcon', 'glacier', 'harbor',
|
|
30
|
+
'island', 'jasper', 'kayak', 'lantern', 'marble', 'nebula', 'oracle', 'prism',
|
|
31
|
+
'quartz', 'rapids', 'summit', 'timber', 'unity', 'vertex', 'willow', 'zenith',
|
|
32
|
+
'arrow', 'bridge', 'canyon', 'dawn', 'echo', 'forest', 'grove', 'hollow',
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Name Generation
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate a memorable plan name (adjective-noun)
|
|
41
|
+
* Example: "agile-beacon", "bold-cipher"
|
|
42
|
+
*/
|
|
43
|
+
function generateMemorableName(): string {
|
|
44
|
+
const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
|
|
45
|
+
const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
|
|
46
|
+
return `${adjective}-${noun}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generate a slug from text (for task-specific naming)
|
|
51
|
+
*/
|
|
52
|
+
function generateSlug(text: string): string {
|
|
53
|
+
return text
|
|
54
|
+
.toLowerCase()
|
|
55
|
+
.replace(/[^a-z0-9\s-]/g, '') // Remove special chars
|
|
56
|
+
.replace(/\s+/g, '-') // Replace spaces with dashes
|
|
57
|
+
.replace(/-+/g, '-') // Collapse multiple dashes
|
|
58
|
+
.slice(0, 30) // Limit length
|
|
59
|
+
.replace(/^-|-$/g, ''); // Trim dashes from ends
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate plan file name
|
|
64
|
+
* Format: <memorable-name>.md (like Claude Code)
|
|
65
|
+
*/
|
|
66
|
+
export function generatePlanFileName(taskDescription?: string): string {
|
|
67
|
+
// Use memorable name like Claude Code (melodic-humming-lampson)
|
|
68
|
+
const name = generateMemorableName();
|
|
69
|
+
return `${name}${PLAN_FILE_EXTENSION}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Directory Management
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get the plans directory path for a project
|
|
78
|
+
*/
|
|
79
|
+
export function getPlansDir(cwd: string): string {
|
|
80
|
+
return path.join(cwd, PLANS_DIR);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Ensure the plans directory exists
|
|
85
|
+
*/
|
|
86
|
+
export async function ensurePlansDir(cwd: string): Promise<string> {
|
|
87
|
+
const plansDir = getPlansDir(cwd);
|
|
88
|
+
|
|
89
|
+
if (!existsSync(plansDir)) {
|
|
90
|
+
await fs.mkdir(plansDir, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return plansDir;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Plan File Operations
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create a new plan file
|
|
102
|
+
*/
|
|
103
|
+
export async function createPlanFile(
|
|
104
|
+
cwd: string,
|
|
105
|
+
taskDescription?: string
|
|
106
|
+
): Promise<PlanFile> {
|
|
107
|
+
const plansDir = await ensurePlansDir(cwd);
|
|
108
|
+
const fileName = generatePlanFileName(taskDescription);
|
|
109
|
+
const filePath = path.join(plansDir, fileName);
|
|
110
|
+
|
|
111
|
+
// Initial plan template
|
|
112
|
+
const initialContent = `# Implementation Plan
|
|
113
|
+
|
|
114
|
+
## Task
|
|
115
|
+
${taskDescription || 'Describe the task here...'}
|
|
116
|
+
|
|
117
|
+
## Analysis
|
|
118
|
+
_Understanding the codebase and requirements..._
|
|
119
|
+
|
|
120
|
+
## Approach
|
|
121
|
+
_Design decisions and implementation strategy..._
|
|
122
|
+
|
|
123
|
+
## Files to Change
|
|
124
|
+
- [ ] File 1 (action)
|
|
125
|
+
- [ ] File 2 (action)
|
|
126
|
+
|
|
127
|
+
## Steps
|
|
128
|
+
1. Step 1
|
|
129
|
+
2. Step 2
|
|
130
|
+
3. Step 3
|
|
131
|
+
|
|
132
|
+
## Pre-approved Permissions
|
|
133
|
+
_Commands that will be allowed during execution..._
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
_Generated by GenCode Plan Mode_
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
const now = new Date();
|
|
140
|
+
await fs.writeFile(filePath, initialContent, 'utf-8');
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
path: filePath,
|
|
144
|
+
content: initialContent,
|
|
145
|
+
createdAt: now,
|
|
146
|
+
updatedAt: now,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Read a plan file
|
|
152
|
+
*/
|
|
153
|
+
export async function readPlanFile(filePath: string): Promise<PlanFile | null> {
|
|
154
|
+
try {
|
|
155
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
156
|
+
const stats = await fs.stat(filePath);
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
path: filePath,
|
|
160
|
+
content,
|
|
161
|
+
createdAt: stats.birthtime,
|
|
162
|
+
updatedAt: stats.mtime,
|
|
163
|
+
};
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Write to a plan file
|
|
171
|
+
*/
|
|
172
|
+
export async function writePlanFile(filePath: string, content: string): Promise<void> {
|
|
173
|
+
// Ensure directory exists
|
|
174
|
+
const dir = path.dirname(filePath);
|
|
175
|
+
if (!existsSync(dir)) {
|
|
176
|
+
await fs.mkdir(dir, { recursive: true });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* List all plan files in the project
|
|
184
|
+
*/
|
|
185
|
+
export async function listPlanFiles(cwd: string): Promise<PlanFile[]> {
|
|
186
|
+
const plansDir = getPlansDir(cwd);
|
|
187
|
+
|
|
188
|
+
if (!existsSync(plansDir)) {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const files = await fs.readdir(plansDir);
|
|
194
|
+
const planFiles: PlanFile[] = [];
|
|
195
|
+
|
|
196
|
+
for (const file of files) {
|
|
197
|
+
if (file.endsWith(PLAN_FILE_EXTENSION)) {
|
|
198
|
+
const filePath = path.join(plansDir, file);
|
|
199
|
+
const planFile = await readPlanFile(filePath);
|
|
200
|
+
if (planFile) {
|
|
201
|
+
planFiles.push(planFile);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Sort by updated time, newest first
|
|
207
|
+
planFiles.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
208
|
+
|
|
209
|
+
return planFiles;
|
|
210
|
+
} catch {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Delete a plan file
|
|
217
|
+
*/
|
|
218
|
+
export async function deletePlanFile(filePath: string): Promise<boolean> {
|
|
219
|
+
try {
|
|
220
|
+
await fs.unlink(filePath);
|
|
221
|
+
return true;
|
|
222
|
+
} catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Plan Content Parsing
|
|
229
|
+
// ============================================================================
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Extract files to change from plan content
|
|
233
|
+
*/
|
|
234
|
+
export function parseFilesToChange(
|
|
235
|
+
content: string
|
|
236
|
+
): Array<{ path: string; action: 'create' | 'modify' | 'delete' }> {
|
|
237
|
+
const files: Array<{ path: string; action: 'create' | 'modify' | 'delete' }> = [];
|
|
238
|
+
|
|
239
|
+
// Look for patterns like:
|
|
240
|
+
// - [ ] src/file.ts (create)
|
|
241
|
+
// - [x] src/file.ts (modify)
|
|
242
|
+
// + src/file.ts (create)
|
|
243
|
+
// ~ src/file.ts (modify)
|
|
244
|
+
// - src/file.ts (delete)
|
|
245
|
+
|
|
246
|
+
const lines = content.split('\n');
|
|
247
|
+
for (const line of lines) {
|
|
248
|
+
// Checkbox format
|
|
249
|
+
let match = line.match(/^[-*]\s*\[[ x]\]\s+([^\s(]+)\s*\((\w+)\)/);
|
|
250
|
+
if (match) {
|
|
251
|
+
const [, filePath, action] = match;
|
|
252
|
+
if (action === 'create' || action === 'modify' || action === 'delete') {
|
|
253
|
+
files.push({ path: filePath, action });
|
|
254
|
+
}
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Symbol format (+, ~, -)
|
|
259
|
+
match = line.match(/^\s*([+~-])\s+([^\s(]+)(?:\s*\((\w+)\))?/);
|
|
260
|
+
if (match) {
|
|
261
|
+
const [, symbol, filePath, explicitAction] = match;
|
|
262
|
+
let action: 'create' | 'modify' | 'delete';
|
|
263
|
+
|
|
264
|
+
if (explicitAction === 'create' || explicitAction === 'modify' || explicitAction === 'delete') {
|
|
265
|
+
action = explicitAction;
|
|
266
|
+
} else {
|
|
267
|
+
action = symbol === '+' ? 'create' : symbol === '~' ? 'modify' : 'delete';
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
files.push({ path: filePath, action });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return files;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Extract pre-approved permissions from plan content
|
|
279
|
+
*/
|
|
280
|
+
export function parsePreApprovedPermissions(content: string): Array<{ tool: 'Bash'; prompt: string }> {
|
|
281
|
+
const permissions: Array<{ tool: 'Bash'; prompt: string }> = [];
|
|
282
|
+
|
|
283
|
+
// Look for patterns like:
|
|
284
|
+
// - Bash: run tests
|
|
285
|
+
// - npm test
|
|
286
|
+
// - npm install
|
|
287
|
+
|
|
288
|
+
const permissionSection = content.match(/## Pre-approved Permissions[\s\S]*?(?=##|$)/i);
|
|
289
|
+
if (!permissionSection) {
|
|
290
|
+
return permissions;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const lines = permissionSection[0].split('\n');
|
|
294
|
+
for (const line of lines) {
|
|
295
|
+
// Skip header and empty lines
|
|
296
|
+
if (line.startsWith('##') || line.startsWith('_') || !line.trim()) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Match "- Bash: description" or "- description"
|
|
301
|
+
const match = line.match(/^[-*]\s+(?:Bash:\s+)?(.+)/);
|
|
302
|
+
if (match) {
|
|
303
|
+
permissions.push({ tool: 'Bash', prompt: match[1].trim() });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return permissions;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Get relative path for display
|
|
312
|
+
*/
|
|
313
|
+
export function getDisplayPath(fullPath: string, cwd: string): string {
|
|
314
|
+
const relativePath = path.relative(cwd, fullPath);
|
|
315
|
+
const home = process.env.HOME || '';
|
|
316
|
+
|
|
317
|
+
if (relativePath.startsWith('..')) {
|
|
318
|
+
// Path is outside cwd, try home-relative
|
|
319
|
+
if (fullPath.startsWith(home)) {
|
|
320
|
+
return '~' + fullPath.slice(home.length);
|
|
321
|
+
}
|
|
322
|
+
return fullPath;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return relativePath;
|
|
326
|
+
}
|