prjct-cli 0.6.0 → 0.7.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 (83) hide show
  1. package/CHANGELOG.md +67 -6
  2. package/CLAUDE.md +442 -36
  3. package/README.md +47 -54
  4. package/bin/prjct +170 -240
  5. package/core/agentic/command-executor.js +113 -0
  6. package/core/agentic/context-builder.js +85 -0
  7. package/core/agentic/prompt-builder.js +86 -0
  8. package/core/agentic/template-loader.js +104 -0
  9. package/core/agentic/tool-registry.js +117 -0
  10. package/core/command-registry.js +106 -62
  11. package/core/commands.js +2030 -2211
  12. package/core/domain/agent-generator.js +118 -0
  13. package/core/domain/analyzer.js +211 -0
  14. package/core/domain/architect-session.js +300 -0
  15. package/core/{agents → infrastructure/agents}/claude-agent.js +16 -13
  16. package/core/{author-detector.js → infrastructure/author-detector.js} +3 -1
  17. package/core/{capability-installer.js → infrastructure/capability-installer.js} +3 -6
  18. package/core/{command-installer.js → infrastructure/command-installer.js} +4 -2
  19. package/core/{config-manager.js → infrastructure/config-manager.js} +4 -4
  20. package/core/{editors-config.js → infrastructure/editors-config.js} +2 -10
  21. package/core/{migrator.js → infrastructure/migrator.js} +34 -19
  22. package/core/{path-manager.js → infrastructure/path-manager.js} +20 -44
  23. package/core/{session-manager.js → infrastructure/session-manager.js} +45 -105
  24. package/core/{update-checker.js → infrastructure/update-checker.js} +67 -67
  25. package/core/{animations-simple.js → utils/animations.js} +3 -23
  26. package/core/utils/date-helper.js +238 -0
  27. package/core/utils/file-helper.js +327 -0
  28. package/core/utils/jsonl-helper.js +206 -0
  29. package/core/{project-capabilities.js → utils/project-capabilities.js} +21 -22
  30. package/core/utils/session-helper.js +277 -0
  31. package/core/{version.js → utils/version.js} +1 -1
  32. package/package.json +4 -12
  33. package/templates/agents/AGENTS.md +101 -27
  34. package/templates/analysis/analyze.md +84 -0
  35. package/templates/commands/analyze.md +9 -2
  36. package/templates/commands/bug.md +79 -0
  37. package/templates/commands/build.md +5 -2
  38. package/templates/commands/cleanup.md +5 -2
  39. package/templates/commands/design.md +5 -2
  40. package/templates/commands/done.md +4 -2
  41. package/templates/commands/feature.md +113 -0
  42. package/templates/commands/fix.md +41 -10
  43. package/templates/commands/git.md +7 -2
  44. package/templates/commands/help.md +2 -2
  45. package/templates/commands/idea.md +14 -5
  46. package/templates/commands/init.md +62 -7
  47. package/templates/commands/next.md +4 -2
  48. package/templates/commands/now.md +4 -2
  49. package/templates/commands/progress.md +27 -5
  50. package/templates/commands/recap.md +39 -10
  51. package/templates/commands/roadmap.md +19 -5
  52. package/templates/commands/ship.md +118 -16
  53. package/templates/commands/status.md +4 -2
  54. package/templates/commands/sync.md +19 -15
  55. package/templates/commands/task.md +4 -2
  56. package/templates/commands/test.md +5 -2
  57. package/templates/commands/workflow.md +4 -2
  58. package/core/agent-generator.js +0 -525
  59. package/core/analyzer.js +0 -600
  60. package/core/animations.js +0 -277
  61. package/core/ascii-graphics.js +0 -433
  62. package/core/git-integration.js +0 -401
  63. package/core/task-schema.js +0 -342
  64. package/core/workflow-engine.js +0 -213
  65. package/core/workflow-prompts.js +0 -192
  66. package/core/workflow-rules.js +0 -147
  67. package/scripts/post-install.js +0 -121
  68. package/scripts/preuninstall.js +0 -94
  69. package/scripts/verify-installation.sh +0 -158
  70. package/templates/agents/be.template.md +0 -27
  71. package/templates/agents/coordinator.template.md +0 -34
  72. package/templates/agents/data.template.md +0 -27
  73. package/templates/agents/devops.template.md +0 -27
  74. package/templates/agents/fe.template.md +0 -27
  75. package/templates/agents/mobile.template.md +0 -27
  76. package/templates/agents/qa.template.md +0 -27
  77. package/templates/agents/scribe.template.md +0 -29
  78. package/templates/agents/security.template.md +0 -27
  79. package/templates/agents/ux.template.md +0 -27
  80. package/templates/commands/context.md +0 -36
  81. package/templates/commands/stuck.md +0 -36
  82. package/templates/examples/natural-language-examples.md +0 -532
  83. /package/core/{agent-detector.js → infrastructure/agent-detector.js} +0 -0
@@ -0,0 +1,118 @@
1
+ const fs = require('fs').promises
2
+ const path = require('path')
3
+ const os = require('os')
4
+
5
+ /**
6
+ * AgentGenerator - Dynamic agent generation for prjct-cli
7
+ *
8
+ * 100% AGENTIC - Claude decides which agents to create based on project analysis.
9
+ * NO predetermined patterns, NO if/else logic, NO assumptions.
10
+ *
11
+ * @version 0.6.0 - Fully agentic refactor
12
+ */
13
+ class AgentGenerator {
14
+ constructor(projectId = null) {
15
+ this.projectId = projectId
16
+
17
+ // Agents are stored in global project directory
18
+ if (projectId) {
19
+ this.outputDir = path.join(os.homedir(), '.prjct-cli', 'projects', projectId, 'agents')
20
+ } else {
21
+ // Fallback for backwards compatibility
22
+ this.outputDir = path.join(os.homedir(), '.prjct-cli', 'agents')
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Generate a single agent dynamically
28
+ * Claude (the LLM) decides which agents to create based on project analysis
29
+ *
30
+ * @param {string} agentName - Descriptive name (e.g., 'go-backend', 'vuejs-frontend', 'elixir-api')
31
+ * @param {Object} config - Agent configuration
32
+ * @param {string} config.role - Agent's role description
33
+ * @param {string} config.expertise - Technologies and skills (specific versions, tools)
34
+ * @param {string} config.responsibilities - What the agent handles in THIS project
35
+ * @param {Object} config.projectContext - Project-specific context (optional)
36
+ * @returns {Promise<void>}
37
+ */
38
+ async generateDynamicAgent(agentName, config) {
39
+ console.log(` 🤖 Generating ${agentName} agent...`)
40
+
41
+ // Ensure output directory exists
42
+ await fs.mkdir(this.outputDir, { recursive: true })
43
+
44
+ // Create agent content
45
+ const content = `# ${config.role || agentName}
46
+
47
+ ## Role
48
+ ${config.role || 'Specialist for this project'}
49
+
50
+ ## Expertise
51
+ ${config.expertise || 'Technologies used in this project'}
52
+
53
+ ## Responsibilities
54
+ ${config.responsibilities || 'Handle specific aspects of development'}
55
+
56
+ ## Project Context
57
+ ${config.projectContext ? JSON.stringify(config.projectContext, null, 2) : 'No additional context'}
58
+
59
+ ## Guidelines
60
+ - Focus on your area of expertise
61
+ - Collaborate with other agents
62
+ - Follow project conventions
63
+ - Ask clarifying questions when needed
64
+ `
65
+
66
+ // Write agent file
67
+ const outputPath = path.join(this.outputDir, `${agentName}.md`)
68
+ await fs.writeFile(outputPath, content, 'utf-8')
69
+
70
+ console.log(` ✅ ${agentName} agent created`)
71
+ }
72
+
73
+ /**
74
+ * Remove agents that are no longer needed
75
+ * @param {Array} requiredAgents - List of agents that should exist
76
+ * @returns {Promise<Array>} List of removed agents
77
+ */
78
+ async cleanupObsoleteAgents(requiredAgents) {
79
+ const removed = []
80
+
81
+ try {
82
+ const files = await fs.readdir(this.outputDir)
83
+ const agentFiles = files.filter((f) => f.endsWith('.md') && !f.startsWith('.'))
84
+
85
+ for (const file of agentFiles) {
86
+ const type = file.replace('.md', '')
87
+
88
+ if (!requiredAgents.includes(type)) {
89
+ const filePath = path.join(this.outputDir, file)
90
+ await fs.unlink(filePath)
91
+ removed.push(type)
92
+ console.log(` 🗑️ ${type.toUpperCase()} agent removed (no longer needed)`)
93
+ }
94
+ }
95
+ } catch (error) {
96
+ console.error('Error during cleanup:', error.message)
97
+ }
98
+
99
+ return removed
100
+ }
101
+
102
+ /**
103
+ * List all existing agents
104
+ * @returns {Promise<Array>} List of agent names
105
+ */
106
+ async listAgents() {
107
+ try {
108
+ const files = await fs.readdir(this.outputDir)
109
+ return files
110
+ .filter((f) => f.endsWith('.md') && !f.startsWith('.'))
111
+ .map((f) => f.replace('.md', ''))
112
+ } catch {
113
+ return []
114
+ }
115
+ }
116
+ }
117
+
118
+ module.exports = AgentGenerator
@@ -0,0 +1,211 @@
1
+ const fs = require('fs').promises
2
+ const path = require('path')
3
+ const { promisify } = require('util')
4
+ const { exec: execCallback } = require('child_process')
5
+ const exec = promisify(execCallback)
6
+
7
+ /**
8
+ * CodebaseAnalyzer - Provides helpers for project analysis
9
+ *
10
+ * 100% AGENTIC - No predetermined patterns or regex detection.
11
+ * Claude reads the codebase and decides what's relevant.
12
+ *
13
+ * This class only provides I/O helpers. All analysis logic
14
+ * is driven by Claude reading templates/analysis/analyze.md
15
+ *
16
+ * @version 0.6.0 - Fully agentic refactor
17
+ */
18
+ class CodebaseAnalyzer {
19
+ constructor() {
20
+ this.projectPath = null
21
+ }
22
+
23
+ /**
24
+ * Initialize analyzer for a project
25
+ * @param {string} projectPath - Project path
26
+ */
27
+ init(projectPath = process.cwd()) {
28
+ this.projectPath = projectPath
29
+ }
30
+
31
+ /**
32
+ * Read package.json if it exists
33
+ * @returns {Promise<Object|null>}
34
+ */
35
+ async readPackageJson() {
36
+ try {
37
+ const packagePath = path.join(this.projectPath, 'package.json')
38
+ const content = await fs.readFile(packagePath, 'utf-8')
39
+ return JSON.parse(content)
40
+ } catch {
41
+ return null
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Read Cargo.toml if it exists
47
+ * @returns {Promise<string|null>}
48
+ */
49
+ async readCargoToml() {
50
+ try {
51
+ const cargoPath = path.join(this.projectPath, 'Cargo.toml')
52
+ return await fs.readFile(cargoPath, 'utf-8')
53
+ } catch {
54
+ return null
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Read requirements.txt if it exists
60
+ * @returns {Promise<string|null>}
61
+ */
62
+ async readRequirements() {
63
+ try {
64
+ const reqPath = path.join(this.projectPath, 'requirements.txt')
65
+ return await fs.readFile(reqPath, 'utf-8')
66
+ } catch {
67
+ return null
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Read go.mod if it exists
73
+ * @returns {Promise<string|null>}
74
+ */
75
+ async readGoMod() {
76
+ try {
77
+ const goModPath = path.join(this.projectPath, 'go.mod')
78
+ return await fs.readFile(goModPath, 'utf-8')
79
+ } catch {
80
+ return null
81
+ }
82
+ }
83
+
84
+ /**
85
+ * List all directories in project root
86
+ * @returns {Promise<string[]>}
87
+ */
88
+ async listDirectories() {
89
+ try {
90
+ const entries = await fs.readdir(this.projectPath, { withFileTypes: true })
91
+ return entries
92
+ .filter((entry) => entry.isDirectory())
93
+ .map((entry) => entry.name)
94
+ .filter((name) => !name.startsWith('.') && name !== 'node_modules')
95
+ } catch {
96
+ return []
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Get git log (last N commits)
102
+ * @param {number} limit - Number of commits
103
+ * @returns {Promise<string>}
104
+ */
105
+ async getGitLog(limit = 50) {
106
+ try {
107
+ const { stdout } = await exec(`git log -n ${limit} --pretty=format:"%h|%an|%ar|%s"`, {
108
+ cwd: this.projectPath,
109
+ })
110
+ return stdout
111
+ } catch {
112
+ return ''
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Get git statistics
118
+ * @returns {Promise<Object>}
119
+ */
120
+ async getGitStats() {
121
+ try {
122
+ const { stdout: totalCommits } = await exec('git rev-list --count HEAD', {
123
+ cwd: this.projectPath,
124
+ })
125
+
126
+ const { stdout: contributors } = await exec('git log --format="%an" | sort -u | wc -l', {
127
+ cwd: this.projectPath,
128
+ })
129
+
130
+ const { stdout: firstCommit } = await exec(
131
+ 'git log --reverse --pretty=format:"%ar" | head -1',
132
+ { cwd: this.projectPath }
133
+ )
134
+
135
+ return {
136
+ totalCommits: parseInt(totalCommits.trim()) || 0,
137
+ contributors: parseInt(contributors.trim()) || 0,
138
+ age: firstCommit.trim() || 'unknown',
139
+ }
140
+ } catch {
141
+ return {
142
+ totalCommits: 0,
143
+ contributors: 0,
144
+ age: 'unknown',
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Count total files (excluding common ignore patterns)
151
+ * @returns {Promise<number>}
152
+ */
153
+ async countFiles() {
154
+ try {
155
+ const { stdout } = await exec(
156
+ 'find . -type f ! -path "*/node_modules/*" ! -path "*/.git/*" ! -path "*/dist/*" | wc -l',
157
+ { cwd: this.projectPath }
158
+ )
159
+ return parseInt(stdout.trim()) || 0
160
+ } catch {
161
+ return 0
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Check if file exists
167
+ * @param {string} filename - Filename to check
168
+ * @returns {Promise<boolean>}
169
+ */
170
+ async fileExists(filename) {
171
+ try {
172
+ await fs.access(path.join(this.projectPath, filename))
173
+ return true
174
+ } catch {
175
+ return false
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Read any file in the project
181
+ * @param {string} relativePath - Path relative to project root
182
+ * @returns {Promise<string|null>}
183
+ */
184
+ async readFile(relativePath) {
185
+ try {
186
+ const fullPath = path.join(this.projectPath, relativePath)
187
+ return await fs.readFile(fullPath, 'utf-8')
188
+ } catch {
189
+ return null
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Find files matching a pattern
195
+ * @param {string} pattern - Glob pattern
196
+ * @returns {Promise<string[]>}
197
+ */
198
+ async findFiles(pattern) {
199
+ try {
200
+ const { stdout } = await exec(
201
+ `find . -type f -name "${pattern}" ! -path "*/node_modules/*" ! -path "*/.git/*"`,
202
+ { cwd: this.projectPath }
203
+ )
204
+ return stdout.trim().split('\n').filter(Boolean)
205
+ } catch {
206
+ return []
207
+ }
208
+ }
209
+ }
210
+
211
+ module.exports = new CodebaseAnalyzer()
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Architect Session Manager
3
+ * Handles conversational state for ARCHITECT MODE (Agent-based, not deterministic)
4
+ */
5
+
6
+ const fs = require('fs').promises
7
+ const path = require('path')
8
+
9
+ class ArchitectSession {
10
+ /**
11
+ * Initialize new architect session
12
+ * @param {string} idea - Project idea
13
+ * @param {string} projectType - Type of project (web, api, mobile, cli, data)
14
+ * @param {string} globalPath - Global project path
15
+ * @returns {Promise<Object>} Session object
16
+ */
17
+ async init(idea, projectType, globalPath) {
18
+ const session = {
19
+ idea,
20
+ projectType,
21
+ active: true,
22
+ startedAt: new Date().toISOString(),
23
+ conversation: [], // Free-form Q&A logging
24
+ answers: {}, // Key insights for plan generation
25
+ }
26
+
27
+ await this.save(session, globalPath)
28
+ return session
29
+ }
30
+
31
+ /**
32
+ * Load active session
33
+ */
34
+ async load(globalPath) {
35
+ try {
36
+ const sessionPath = path.join(globalPath, 'planning', 'architect-session.json')
37
+ const content = await fs.readFile(sessionPath, 'utf-8')
38
+ return JSON.parse(content)
39
+ } catch {
40
+ return null
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Save session to disk
46
+ */
47
+ async save(session, globalPath) {
48
+ const planningDir = path.join(globalPath, 'planning')
49
+ await fs.mkdir(planningDir, { recursive: true })
50
+
51
+ const sessionPath = path.join(planningDir, 'architect-session.json')
52
+ await fs.writeFile(sessionPath, JSON.stringify(session, null, 2))
53
+ }
54
+
55
+ /**
56
+ * Log a Q&A pair to the conversation
57
+ * @param {string} question - The question asked by Claude
58
+ * @param {string} answer - User's answer
59
+ * @param {string} globalPath - Global project path
60
+ */
61
+ async logQA(question, answer, globalPath) {
62
+ const session = await this.load(globalPath)
63
+
64
+ if (!session || !session.active) {
65
+ throw new Error('No active architect session')
66
+ }
67
+
68
+ session.conversation.push({
69
+ question,
70
+ answer,
71
+ timestamp: new Date().toISOString(),
72
+ })
73
+
74
+ await this.save(session, globalPath)
75
+ }
76
+
77
+ /**
78
+ * Save key insight for plan generation
79
+ * @param {string} key - Insight key (e.g., 'language', 'framework', 'database')
80
+ * @param {string} value - Insight value
81
+ * @param {string} globalPath - Global project path
82
+ */
83
+ async saveInsight(key, value, globalPath) {
84
+ const session = await this.load(globalPath)
85
+
86
+ if (!session || !session.active) {
87
+ throw new Error('No active architect session')
88
+ }
89
+
90
+ session.answers[key] = value
91
+ await this.save(session, globalPath)
92
+ }
93
+
94
+ /**
95
+ * Complete session and generate plan
96
+ * @param {string} globalPath - Global project path
97
+ * @returns {Promise<Object>} Summary object
98
+ */
99
+ async complete(globalPath) {
100
+ const session = await this.load(globalPath)
101
+
102
+ if (!session || !session.active) {
103
+ throw new Error('No active architect session')
104
+ }
105
+
106
+ // Generate plan MD
107
+ await this.generatePlan(session, globalPath)
108
+
109
+ // Mark session as complete
110
+ session.active = false
111
+ session.completedAt = new Date().toISOString()
112
+ await this.save(session, globalPath)
113
+
114
+ return this.buildSummary(session)
115
+ }
116
+
117
+ /**
118
+ * Generate architect plan MD file
119
+ */
120
+ async generatePlan(session, globalPath) {
121
+ const plan = this.buildPlanMarkdown(session)
122
+
123
+ const planPath = path.join(globalPath, 'planning', 'architect-session.md')
124
+ await fs.writeFile(planPath, plan)
125
+ }
126
+
127
+ /**
128
+ * Build plan markdown content
129
+ */
130
+ buildPlanMarkdown(session) {
131
+ const { projectType, idea, conversation, answers } = session
132
+
133
+ // Build conversation log
134
+ const conversationLog = conversation
135
+ .map((qa, i) => `### Q${i + 1}: ${qa.question}\n**A**: ${qa.answer}\n\n_${qa.timestamp}_`)
136
+ .join('\n\n')
137
+
138
+ // Build stack summary from answers
139
+ const stackSummary = this.buildStackSummary(answers)
140
+
141
+ // Build Context7 queries
142
+ const context7Queries = this.buildContext7Queries(answers)
143
+
144
+ // Build implementation steps
145
+ const steps = this.buildImplementationSteps(session)
146
+
147
+ return `# ARCHITECT SESSION: ${idea}
148
+
149
+ ## Project Idea
150
+ ${idea}
151
+
152
+ ## Project Type
153
+ ${projectType}
154
+
155
+ ## Discovery Conversation
156
+
157
+ ${conversationLog}
158
+
159
+ ## Architecture Summary
160
+
161
+ **Stack:**
162
+ ${stackSummary}
163
+
164
+ ## Implementation Plan
165
+
166
+ **Context7 Queries:**
167
+ ${context7Queries.map((q) => `- "${q}"`).join('\n')}
168
+
169
+ **Implementation Steps:**
170
+ ${steps.map((step, i) => `${i + 1}. ${step}`).join('\n')}
171
+
172
+ ## Execution
173
+
174
+ This plan is ready to be executed.
175
+
176
+ **To generate code:**
177
+ \`\`\`
178
+ /p:architect execute
179
+ \`\`\`
180
+
181
+ The agent will:
182
+ 1. Read this architectural plan
183
+ 2. Use Context7 to fetch official documentation
184
+ 3. Generate project structure following best practices
185
+ 4. Create starter files with boilerplate code
186
+
187
+ ---
188
+ Generated: ${new Date().toISOString()}
189
+ `
190
+ }
191
+
192
+ /**
193
+ * Build stack summary from answers
194
+ */
195
+ buildStackSummary(answers) {
196
+ const parts = []
197
+
198
+ for (const [key, value] of Object.entries(answers)) {
199
+ if (value && value !== 'Ninguna' && value !== 'None' && value !== 'Otro') {
200
+ parts.push(`- **${key}**: ${value}`)
201
+ }
202
+ }
203
+
204
+ return parts.length > 0 ? parts.join('\n') : '- To be determined during implementation'
205
+ }
206
+
207
+ /**
208
+ * Build Context7 queries from answers
209
+ */
210
+ buildContext7Queries(answers) {
211
+ const queries = []
212
+
213
+ if (answers.framework) {
214
+ queries.push(`${answers.framework} getting started`)
215
+ queries.push(`${answers.framework} project structure`)
216
+ }
217
+
218
+ if (answers.language && answers.framework) {
219
+ queries.push(`${answers.language} ${answers.framework} best practices`)
220
+ }
221
+
222
+ if (answers.database && answers.language) {
223
+ queries.push(`${answers.database} ${answers.language} integration`)
224
+ }
225
+
226
+ if (answers.auth) {
227
+ queries.push(`${answers.auth} implementation ${answers.language || ''}`.trim())
228
+ }
229
+
230
+ // Always include general queries if specific ones not available
231
+ if (queries.length === 0 && answers.language) {
232
+ queries.push(`${answers.language} project structure`)
233
+ queries.push(`${answers.language} best practices`)
234
+ }
235
+
236
+ return queries
237
+ }
238
+
239
+ /**
240
+ * Build implementation steps from session
241
+ */
242
+ buildImplementationSteps(session) {
243
+ const { answers } = session
244
+ const steps = []
245
+
246
+ // Generic steps - Claude will refine during execution
247
+ if (answers.language) {
248
+ steps.push(`Initialize ${answers.language} project`)
249
+ }
250
+
251
+ if (answers.framework) {
252
+ steps.push(`Setup ${answers.framework}`)
253
+ }
254
+
255
+ if (answers.database) {
256
+ steps.push(`Configure ${answers.database}`)
257
+ }
258
+
259
+ if (answers.auth) {
260
+ steps.push(`Implement ${answers.auth}`)
261
+ }
262
+
263
+ steps.push('Create project structure')
264
+ steps.push('Generate starter files')
265
+
266
+ if (answers.deployment) {
267
+ steps.push(`Setup ${answers.deployment} configuration`)
268
+ }
269
+
270
+ return steps
271
+ }
272
+
273
+ /**
274
+ * Build summary of session
275
+ */
276
+ buildSummary(session) {
277
+ return {
278
+ idea: session.idea,
279
+ projectType: session.projectType,
280
+ conversationLength: session.conversation.length,
281
+ insights: Object.keys(session.answers).length,
282
+ startedAt: session.startedAt,
283
+ completedAt: session.completedAt || new Date().toISOString(),
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Clear architect session
289
+ */
290
+ async clear(globalPath) {
291
+ try {
292
+ const sessionPath = path.join(globalPath, 'planning', 'architect-session.json')
293
+ await fs.unlink(sessionPath)
294
+ } catch {
295
+ // Ignore if doesn't exist
296
+ }
297
+ }
298
+ }
299
+
300
+ module.exports = new ArchitectSession()
@@ -39,9 +39,7 @@ class ClaudeAgent {
39
39
  if (global.mcp && global.mcp.filesystem) {
40
40
  return await global.mcp.filesystem.read(filePath)
41
41
  }
42
- } catch (e) {
43
-
44
- }
42
+ } catch (e) {}
45
43
 
46
44
  return await fs.readFile(filePath, 'utf8')
47
45
  }
@@ -54,9 +52,7 @@ class ClaudeAgent {
54
52
  if (global.mcp && global.mcp.filesystem) {
55
53
  return await global.mcp.filesystem.write(filePath, content)
56
54
  }
57
- } catch (e) {
58
-
59
- }
55
+ } catch (e) {}
60
56
 
61
57
  await fs.writeFile(filePath, content, 'utf8')
62
58
  }
@@ -69,9 +65,7 @@ class ClaudeAgent {
69
65
  if (global.mcp && global.mcp.filesystem) {
70
66
  return await global.mcp.filesystem.list(dirPath)
71
67
  }
72
- } catch (e) {
73
-
74
- }
68
+ } catch (e) {}
75
69
 
76
70
  return await fs.readdir(dirPath)
77
71
  }
@@ -131,7 +125,11 @@ ${data.recentActivity ? '\n' + data.recentActivity : ''}`
131
125
  */
132
126
  formatProgress(data) {
133
127
  const trend =
134
- data.velocity > data.previousVelocity ? '📈' : data.velocity < data.previousVelocity ? '📉' : '➡️'
128
+ data.velocity > data.previousVelocity
129
+ ? '📈'
130
+ : data.velocity < data.previousVelocity
131
+ ? '📉'
132
+ : '➡️'
135
133
 
136
134
  return `📊 ${data.period}
137
135
 
@@ -145,14 +143,16 @@ ${data.recentFeatures || ''}`
145
143
  */
146
144
  getHelpContent(issue) {
147
145
  const helps = {
148
- debugging: '🔍 1. Isolate code causing error\n2. Add logs at key points\n3. Search exact error message',
146
+ debugging:
147
+ '🔍 1. Isolate code causing error\n2. Add logs at key points\n3. Search exact error message',
149
148
  design: '🎨 1. Define problem clearly\n2. Start with simplest solution\n3. Ship MVP, iterate',
150
149
  performance:
151
150
  '⚡ 1. Profile/measure first\n2. Optimize slowest parts\n3. Cache expensive operations',
152
151
  default: '💡 1. Break into smaller tasks\n2. Start with simplest part\n3. Ship it',
153
152
  }
154
153
 
155
- const helpType = Object.keys(helps).find((key) => issue.toLowerCase().includes(key)) || 'default'
154
+ const helpType =
155
+ Object.keys(helps).find((key) => issue.toLowerCase().includes(key)) || 'default'
156
156
  return helps[helpType]
157
157
  }
158
158
 
@@ -194,9 +194,12 @@ Or type /p:help to see all options`,
194
194
  Or: /p:now | /p:task | /p:idea`,
195
195
  }
196
196
 
197
- return suggestions[context] || `What would you like to do?
197
+ return (
198
+ suggestions[context] ||
199
+ `What would you like to do?
198
200
 
199
201
  Type /p:help to see all options`
202
+ )
200
203
  }
201
204
 
202
205
  /**
@@ -175,7 +175,9 @@ class AuthorDetector {
175
175
  const recommendations = []
176
176
 
177
177
  if (!hasGitHub && !author.github) {
178
- recommendations.push('Install GitHub CLI (gh) for better collaboration support: https://cli.github.com/')
178
+ recommendations.push(
179
+ 'Install GitHub CLI (gh) for better collaboration support: https://cli.github.com/'
180
+ )
179
181
  }
180
182
 
181
183
  if (!hasGit) {