prjct-cli 0.4.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 (71) hide show
  1. package/CHANGELOG.md +312 -0
  2. package/CLAUDE.md +300 -0
  3. package/LICENSE +21 -0
  4. package/README.md +424 -0
  5. package/bin/prjct +214 -0
  6. package/core/agent-detector.js +249 -0
  7. package/core/agents/claude-agent.js +250 -0
  8. package/core/agents/codex-agent.js +256 -0
  9. package/core/agents/terminal-agent.js +465 -0
  10. package/core/analyzer.js +596 -0
  11. package/core/animations-simple.js +240 -0
  12. package/core/animations.js +277 -0
  13. package/core/author-detector.js +218 -0
  14. package/core/capability-installer.js +190 -0
  15. package/core/command-installer.js +775 -0
  16. package/core/commands.js +2050 -0
  17. package/core/config-manager.js +335 -0
  18. package/core/migrator.js +784 -0
  19. package/core/path-manager.js +324 -0
  20. package/core/project-capabilities.js +144 -0
  21. package/core/session-manager.js +439 -0
  22. package/core/version.js +107 -0
  23. package/core/workflow-engine.js +213 -0
  24. package/core/workflow-prompts.js +192 -0
  25. package/core/workflow-rules.js +147 -0
  26. package/package.json +80 -0
  27. package/scripts/install.sh +433 -0
  28. package/scripts/verify-installation.sh +158 -0
  29. package/templates/agents/AGENTS.md +164 -0
  30. package/templates/commands/analyze.md +125 -0
  31. package/templates/commands/cleanup.md +102 -0
  32. package/templates/commands/context.md +105 -0
  33. package/templates/commands/design.md +113 -0
  34. package/templates/commands/done.md +44 -0
  35. package/templates/commands/fix.md +87 -0
  36. package/templates/commands/git.md +79 -0
  37. package/templates/commands/help.md +72 -0
  38. package/templates/commands/idea.md +50 -0
  39. package/templates/commands/init.md +237 -0
  40. package/templates/commands/next.md +74 -0
  41. package/templates/commands/now.md +35 -0
  42. package/templates/commands/progress.md +92 -0
  43. package/templates/commands/recap.md +86 -0
  44. package/templates/commands/roadmap.md +107 -0
  45. package/templates/commands/ship.md +41 -0
  46. package/templates/commands/stuck.md +48 -0
  47. package/templates/commands/task.md +97 -0
  48. package/templates/commands/test.md +94 -0
  49. package/templates/commands/workflow.md +224 -0
  50. package/templates/examples/natural-language-examples.md +320 -0
  51. package/templates/mcp-config.json +8 -0
  52. package/templates/workflows/analyze.md +159 -0
  53. package/templates/workflows/cleanup.md +73 -0
  54. package/templates/workflows/context.md +72 -0
  55. package/templates/workflows/design.md +88 -0
  56. package/templates/workflows/done.md +20 -0
  57. package/templates/workflows/fix.md +201 -0
  58. package/templates/workflows/git.md +192 -0
  59. package/templates/workflows/help.md +13 -0
  60. package/templates/workflows/idea.md +22 -0
  61. package/templates/workflows/init.md +80 -0
  62. package/templates/workflows/natural-language-handler.md +183 -0
  63. package/templates/workflows/next.md +44 -0
  64. package/templates/workflows/now.md +19 -0
  65. package/templates/workflows/progress.md +113 -0
  66. package/templates/workflows/recap.md +66 -0
  67. package/templates/workflows/roadmap.md +95 -0
  68. package/templates/workflows/ship.md +18 -0
  69. package/templates/workflows/stuck.md +25 -0
  70. package/templates/workflows/task.md +109 -0
  71. package/templates/workflows/test.md +243 -0
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Workflow Engine
3
+ * Orchestrates adaptive agent workflows based on project capabilities
4
+ */
5
+
6
+ const fs = require('fs').promises
7
+ const path = require('path')
8
+ const capabilities = require('./project-capabilities')
9
+ const rules = require('./workflow-rules')
10
+
11
+ class WorkflowEngine {
12
+ /**
13
+ * Initialize workflow for task
14
+ * @param {string} task - Task description
15
+ * @param {string} type - Workflow type (ui, api, bug, refactor, feature)
16
+ * @param {string} dataPath - Project data path
17
+ * @returns {Promise<Object>} Workflow object
18
+ */
19
+ async init(task, type, dataPath) {
20
+ // Detect project capabilities
21
+ const projectPath = path.dirname(path.dirname(dataPath))
22
+ const caps = await capabilities.detect(projectPath)
23
+
24
+ // Get base workflow
25
+ const base = rules[type] || rules.ui
26
+
27
+ // Map all steps - mark for prompting if capability missing
28
+ const steps = base.map((s, index) => ({
29
+ ...s,
30
+ index,
31
+ status: 'pending',
32
+ skipped: false,
33
+ needsPrompt: !s.required && s.prompt && !caps[s.needs],
34
+ }))
35
+
36
+ // Track which capabilities are missing
37
+ const missingCapabilities = base
38
+ .filter(s => !s.required && s.needs && !caps[s.needs])
39
+ .map(s => s.needs)
40
+
41
+ const workflow = {
42
+ task,
43
+ type,
44
+ caps,
45
+ steps,
46
+ missingCapabilities,
47
+ current: 0,
48
+ active: true,
49
+ createdAt: new Date().toISOString(),
50
+ }
51
+
52
+ await this.save(workflow, dataPath)
53
+ return workflow
54
+ }
55
+
56
+ /**
57
+ * Get current step info
58
+ */
59
+ async getCurrent(dataPath) {
60
+ const wf = await this.load(dataPath)
61
+ if (!wf || !wf.active) return null
62
+
63
+ return wf.steps[wf.current]
64
+ }
65
+
66
+ /**
67
+ * Advance to next step
68
+ */
69
+ async next(dataPath) {
70
+ const wf = await this.load(dataPath)
71
+ if (!wf) return null
72
+
73
+ // Mark current step as completed
74
+ wf.steps[wf.current].status = 'completed'
75
+ wf.steps[wf.current].completedAt = new Date().toISOString()
76
+
77
+ // Move to next
78
+ wf.current++
79
+
80
+ // Check if workflow complete
81
+ if (wf.current >= wf.steps.length) {
82
+ wf.active = false
83
+ wf.completedAt = new Date().toISOString()
84
+ await this.save(wf, dataPath)
85
+ return null
86
+ }
87
+
88
+ // Mark next step as in progress
89
+ wf.steps[wf.current].status = 'in_progress'
90
+ wf.steps[wf.current].startedAt = new Date().toISOString()
91
+
92
+ await this.save(wf, dataPath)
93
+ return wf.steps[wf.current]
94
+ }
95
+
96
+ /**
97
+ * Skip current step
98
+ */
99
+ async skip(dataPath, reason = 'User skipped') {
100
+ const wf = await this.load(dataPath)
101
+ if (!wf) return null
102
+
103
+ wf.steps[wf.current].skipped = true
104
+ wf.steps[wf.current].status = 'skipped'
105
+ wf.steps[wf.current].skipReason = reason
106
+ wf.steps[wf.current].skippedAt = new Date().toISOString()
107
+
108
+ return await this.next(dataPath)
109
+ }
110
+
111
+ /**
112
+ * Insert installation step before current step
113
+ */
114
+ async insertInstall(dataPath, installTask) {
115
+ const wf = await this.load(dataPath)
116
+ if (!wf) return null
117
+
118
+ const currentIndex = wf.current
119
+
120
+ // Insert install task at current position
121
+ wf.steps.splice(currentIndex, 0, {
122
+ ...installTask,
123
+ index: currentIndex,
124
+ status: 'in_progress',
125
+ insertedAt: new Date().toISOString(),
126
+ })
127
+
128
+ // Reindex all subsequent steps
129
+ for (let i = currentIndex + 1; i < wf.steps.length; i++) {
130
+ wf.steps[i].index = i
131
+ }
132
+
133
+ await this.save(wf, dataPath)
134
+ return wf.steps[currentIndex]
135
+ }
136
+
137
+ /**
138
+ * Classify task type from description
139
+ * @param {string} text - Task description
140
+ * @returns {string} Workflow type
141
+ */
142
+ classify(text) {
143
+ const t = text.toLowerCase()
144
+
145
+ if (/button|form|modal|card|component|menu|nav|input/.test(t)) return 'ui'
146
+ if (/endpoint|api|service|route|controller/.test(t)) return 'api'
147
+ if (/bug|fix|error|issue|broken/.test(t)) return 'bug'
148
+ if (/refactor|improve|optimize|clean/.test(t)) return 'refactor'
149
+ if (/feature|functionality|module/.test(t)) return 'feature'
150
+
151
+ return 'ui' // Default
152
+ }
153
+
154
+ /**
155
+ * Get workflow status
156
+ */
157
+ async getStatus(dataPath) {
158
+ const wf = await this.load(dataPath)
159
+ if (!wf) return null
160
+
161
+ return {
162
+ task: wf.task,
163
+ type: wf.type,
164
+ active: wf.active,
165
+ current: wf.current,
166
+ total: wf.steps.length,
167
+ steps: wf.steps.map(s => ({
168
+ name: s.name,
169
+ status: s.status,
170
+ agent: s.agent,
171
+ })),
172
+ skipped: wf.skipped,
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Load workflow from file
178
+ */
179
+ async load(dataPath) {
180
+ try {
181
+ const workflowPath = path.join(dataPath, 'workflow', 'state.json')
182
+ const content = await fs.readFile(workflowPath, 'utf8')
183
+ return JSON.parse(content)
184
+ } catch {
185
+ return null
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Save workflow to file
191
+ */
192
+ async save(workflow, dataPath) {
193
+ const workflowDir = path.join(dataPath, 'workflow')
194
+ await fs.mkdir(workflowDir, { recursive: true })
195
+
196
+ const workflowPath = path.join(workflowDir, 'state.json')
197
+ await fs.writeFile(workflowPath, JSON.stringify(workflow, null, 2))
198
+ }
199
+
200
+ /**
201
+ * Clear workflow
202
+ */
203
+ async clear(dataPath) {
204
+ try {
205
+ const workflowPath = path.join(dataPath, 'workflow', 'state.json')
206
+ await fs.unlink(workflowPath)
207
+ } catch {
208
+ // Ignore if doesn't exist
209
+ }
210
+ }
211
+ }
212
+
213
+ module.exports = new WorkflowEngine()
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Interactive workflow prompts for missing capabilities
3
+ * Asks users before skipping or installing tools
4
+ */
5
+
6
+ class WorkflowPrompts {
7
+ /**
8
+ * Get recommendation based on detected stack
9
+ */
10
+ getRecommendation(stepName, projectInfo = {}) {
11
+ const recommendations = {
12
+ design: {
13
+ tools: ['Figma plugin', 'Storybook', 'Design tokens'],
14
+ install: 'npx storybook@latest init',
15
+ reason: 'Component documentation and design system',
16
+ },
17
+ test: {
18
+ tools: this.getTestRecommendation(projectInfo),
19
+ install: this.getTestInstallCommand(projectInfo),
20
+ reason: 'Quality assurance and regression prevention',
21
+ },
22
+ docs: {
23
+ tools: ['JSDoc', 'TypeDoc', 'Docusaurus'],
24
+ install: 'npm install -D jsdoc',
25
+ reason: 'API documentation and code examples',
26
+ },
27
+ }
28
+
29
+ return recommendations[stepName] || null
30
+ }
31
+
32
+ /**
33
+ * Detect test framework based on project stack
34
+ */
35
+ getTestRecommendation(projectInfo) {
36
+ const { framework, typescript } = projectInfo
37
+
38
+ if (framework === 'react') {
39
+ return typescript
40
+ ? ['Vitest + Testing Library', 'Jest + Testing Library']
41
+ : ['Jest + Testing Library', 'Vitest']
42
+ }
43
+
44
+ if (framework === 'vue') {
45
+ return ['Vitest', '@vue/test-utils']
46
+ }
47
+
48
+ if (framework === 'angular') {
49
+ return ['Jasmine + Karma', 'Jest']
50
+ }
51
+
52
+ // Default Node.js
53
+ return typescript
54
+ ? ['Vitest', 'Jest with ts-jest']
55
+ : ['Jest', 'Vitest', 'Mocha + Chai']
56
+ }
57
+
58
+ /**
59
+ * Get install command based on stack
60
+ */
61
+ getTestInstallCommand(projectInfo) {
62
+ const { framework, typescript } = projectInfo
63
+
64
+ if (framework === 'react') {
65
+ return typescript
66
+ ? 'npm install -D vitest @testing-library/react @testing-library/jest-dom'
67
+ : 'npm install -D jest @testing-library/react @testing-library/jest-dom'
68
+ }
69
+
70
+ if (framework === 'vue') {
71
+ return 'npm install -D vitest @vue/test-utils'
72
+ }
73
+
74
+ if (framework === 'angular') {
75
+ return 'npm install -D jest @types/jest ts-jest'
76
+ }
77
+
78
+ return typescript
79
+ ? 'npm install -D vitest'
80
+ : 'npm install -D jest'
81
+ }
82
+
83
+ /**
84
+ * Detect project stack from package.json
85
+ */
86
+ async detectStack(projectPath) {
87
+ const fs = require('fs').promises
88
+ const path = require('path')
89
+
90
+ try {
91
+ const pkgPath = path.join(projectPath, 'package.json')
92
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'))
93
+
94
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies }
95
+
96
+ return {
97
+ framework: this.detectFramework(deps),
98
+ typescript: 'typescript' in deps,
99
+ bundler: this.detectBundler(deps),
100
+ runtime: this.detectRuntime(deps),
101
+ }
102
+ } catch {
103
+ return { framework: 'node', typescript: false }
104
+ }
105
+ }
106
+
107
+ detectFramework(deps) {
108
+ if ('react' in deps) return 'react'
109
+ if ('vue' in deps) return 'vue'
110
+ if ('@angular/core' in deps) return 'angular'
111
+ if ('next' in deps) return 'next'
112
+ if ('nuxt' in deps) return 'nuxt'
113
+ return 'node'
114
+ }
115
+
116
+ detectBundler(deps) {
117
+ if ('vite' in deps) return 'vite'
118
+ if ('webpack' in deps) return 'webpack'
119
+ if ('esbuild' in deps) return 'esbuild'
120
+ return null
121
+ }
122
+
123
+ detectRuntime(deps) {
124
+ if ('bun' in deps) return 'bun'
125
+ if ('deno' in deps) return 'deno'
126
+ return 'node'
127
+ }
128
+
129
+ /**
130
+ * Build prompt message for missing capability
131
+ */
132
+ async buildPrompt(step, capabilities, projectPath) {
133
+ const stack = await this.detectStack(projectPath)
134
+ const rec = this.getRecommendation(step.needs, stack)
135
+
136
+ if (!rec) {
137
+ return {
138
+ message: `⚠️ Step "${step.name}" requires ${step.needs} capability\n\nOptions:\n1. Skip this step\n2. Continue without ${step.needs}\n3. Pause workflow`,
139
+ options: ['skip', 'continue', 'pause'],
140
+ recommendation: null,
141
+ }
142
+ }
143
+
144
+ const toolsList = rec.tools.join(', ')
145
+
146
+ return {
147
+ message: `⚠️ Missing ${step.needs} capability for "${step.name}" step\n\n` +
148
+ `📋 Recommended: ${toolsList}\n` +
149
+ `💡 Reason: ${rec.reason}\n\n` +
150
+ 'Options:\n' +
151
+ `1. Install recommended (${rec.install})\n` +
152
+ '2. Skip this step\n' +
153
+ `3. Continue without ${step.needs}\n` +
154
+ '4. Pause workflow',
155
+ options: ['install', 'skip', 'continue', 'pause'],
156
+ recommendation: rec,
157
+ stack,
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Parse user choice from response
163
+ */
164
+ parseChoice(response) {
165
+ const lower = response.toLowerCase().trim()
166
+
167
+ if (lower.match(/^(1|install|yes|y)/)) return 'install'
168
+ if (lower.match(/^(2|skip|s)/)) return 'skip'
169
+ if (lower.match(/^(3|continue|c)/)) return 'continue'
170
+ if (lower.match(/^(4|pause|p)/)) return 'pause'
171
+
172
+ return 'skip' // Default to skip if unclear
173
+ }
174
+
175
+ /**
176
+ * Create installation task for workflow
177
+ */
178
+ createInstallTask(step, recommendation) {
179
+ return {
180
+ name: `install-${step.needs}`,
181
+ agent: 'devops',
182
+ action: `Install ${step.needs}: ${recommendation.install}`,
183
+ required: true,
184
+ type: 'install',
185
+ install: recommendation.install,
186
+ capability: step.needs,
187
+ reason: recommendation.reason,
188
+ }
189
+ }
190
+ }
191
+
192
+ module.exports = new WorkflowPrompts()
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Workflow Rules
3
+ * Defines workflows by task type with capability requirements
4
+ */
5
+
6
+ module.exports = {
7
+ // UI Component workflow
8
+ ui: [
9
+ {
10
+ name: 'design',
11
+ agent: 'frontend',
12
+ action: 'Create component design',
13
+ required: false,
14
+ needs: 'design',
15
+ prompt: true,
16
+ },
17
+ {
18
+ name: 'dev',
19
+ agent: 'frontend',
20
+ action: 'Implement component',
21
+ required: true,
22
+ },
23
+ {
24
+ name: 'test',
25
+ agent: 'qa',
26
+ action: 'Create tests',
27
+ required: false,
28
+ needs: 'test',
29
+ retry: 3,
30
+ prompt: true,
31
+ },
32
+ {
33
+ name: 'docs',
34
+ agent: 'scribe',
35
+ action: 'Document component',
36
+ required: false,
37
+ needs: 'docs',
38
+ prompt: true,
39
+ },
40
+ ],
41
+
42
+ // API Endpoint workflow
43
+ api: [
44
+ {
45
+ name: 'dev',
46
+ agent: 'backend',
47
+ action: 'Implement endpoint',
48
+ required: true,
49
+ },
50
+ {
51
+ name: 'test',
52
+ agent: 'qa',
53
+ action: 'Create API tests',
54
+ required: false,
55
+ needs: 'test',
56
+ retry: 3,
57
+ prompt: true,
58
+ },
59
+ {
60
+ name: 'docs',
61
+ agent: 'scribe',
62
+ action: 'Document API',
63
+ required: false,
64
+ needs: 'docs',
65
+ prompt: true,
66
+ },
67
+ ],
68
+
69
+ // Bug Fix workflow
70
+ bug: [
71
+ {
72
+ name: 'analyze',
73
+ agent: 'analyzer',
74
+ action: 'Analyze bug',
75
+ required: true,
76
+ },
77
+ {
78
+ name: 'fix',
79
+ agent: 'auto',
80
+ action: 'Fix bug',
81
+ required: true,
82
+ },
83
+ {
84
+ name: 'test',
85
+ agent: 'qa',
86
+ action: 'Verify fix',
87
+ required: false,
88
+ needs: 'test',
89
+ retry: 3,
90
+ prompt: true,
91
+ },
92
+ ],
93
+
94
+ // Refactor workflow
95
+ refactor: [
96
+ {
97
+ name: 'refactor',
98
+ agent: 'refactorer',
99
+ action: 'Refactor code',
100
+ required: true,
101
+ },
102
+ {
103
+ name: 'test',
104
+ agent: 'qa',
105
+ action: 'Verify refactor',
106
+ required: false,
107
+ needs: 'test',
108
+ retry: 3,
109
+ prompt: true,
110
+ },
111
+ ],
112
+
113
+ // Feature workflow (complete feature)
114
+ feature: [
115
+ {
116
+ name: 'design',
117
+ agent: 'architect',
118
+ action: 'Design feature',
119
+ required: false,
120
+ needs: 'design',
121
+ prompt: true,
122
+ },
123
+ {
124
+ name: 'dev',
125
+ agent: 'auto',
126
+ action: 'Implement feature',
127
+ required: true,
128
+ },
129
+ {
130
+ name: 'test',
131
+ agent: 'qa',
132
+ action: 'Test feature',
133
+ required: false,
134
+ needs: 'test',
135
+ retry: 3,
136
+ prompt: true,
137
+ },
138
+ {
139
+ name: 'docs',
140
+ agent: 'scribe',
141
+ action: 'Document feature',
142
+ required: false,
143
+ needs: 'docs',
144
+ prompt: true,
145
+ },
146
+ ],
147
+ }
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "prjct-cli",
3
+ "version": "0.4.0",
4
+ "description": "AI-integrated project management for indie hackers - works with Claude Code, Cursor, and Warp",
5
+ "main": "core/index.js",
6
+ "bin": {
7
+ "prjct": "./bin/prjct"
8
+ },
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "scripts": {
13
+ "install-global": "./scripts/install.sh",
14
+ "test": "echo 'No tests configured'",
15
+ "lint": "eslint \"**/*.js\" --ignore-pattern \"node_modules/**\" --ignore-pattern \"website/**\"",
16
+ "lint:fix": "eslint \"**/*.js\" --fix --ignore-pattern \"node_modules/**\" --ignore-pattern \"website/**\"",
17
+ "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\" --config .config/.prettierrc --ignore-path .config/.prettierignore",
18
+ "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,md}\" --config .config/.prettierrc --ignore-path .config/.prettierignore",
19
+ "website:dev": "cd website && npm run dev",
20
+ "website:build": "cd website && npm run build",
21
+ "website:preview": "cd website && npm run preview",
22
+ "website:install": "cd website && npm install"
23
+ },
24
+ "keywords": [
25
+ "project-management",
26
+ "indie-hacker",
27
+ "ai-assistant",
28
+ "claude-code",
29
+ "cursor",
30
+ "warp",
31
+ "productivity"
32
+ ],
33
+ "author": "prjct.dev",
34
+ "license": "MIT",
35
+ "dependencies": {
36
+ "chalk": "^4.1.2",
37
+ "commander": "^11.0.0",
38
+ "ora": "^5.4.1",
39
+ "prompts": "^2.4.2"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^20.0.0",
43
+ "eslint": "^8.57.1",
44
+ "eslint-config-standard": "^17.1.0",
45
+ "eslint-plugin-import": "^2.32.0",
46
+ "eslint-plugin-n": "^16.6.2",
47
+ "eslint-plugin-promise": "^6.6.0",
48
+ "prettier": "^3.6.2",
49
+ "prettier-plugin-tailwindcss": "^0.6.14",
50
+ "typescript": "^5.0.0"
51
+ },
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "https://github.com/jlopezlira/prjct-cli"
55
+ },
56
+ "bugs": {
57
+ "url": "https://github.com/jlopezlira/prjct-cli/issues"
58
+ },
59
+ "homepage": "https://prjct.dev",
60
+ "engines": {
61
+ "node": ">=18.0.0"
62
+ },
63
+ "files": [
64
+ "bin/",
65
+ "core/",
66
+ "templates/",
67
+ "scripts/install.sh",
68
+ "scripts/verify-installation.sh",
69
+ "LICENSE",
70
+ "README.md",
71
+ "CHANGELOG.md",
72
+ "CLAUDE.md"
73
+ ],
74
+ "trustedDependencies": [
75
+ "chalk",
76
+ "commander",
77
+ "ora",
78
+ "prompts"
79
+ ]
80
+ }