javi-forge 0.1.0 → 1.0.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/.releaserc +2 -1
- package/README.md +143 -31
- package/ai-config/commands/workflows/diagnose.md +70 -0
- package/ai-config/commands/workflows/discover.md +86 -0
- package/dist/commands/doctor.js +24 -1
- package/dist/commands/init.js +48 -1
- package/dist/commands/llmstxt.d.ts +9 -0
- package/dist/commands/llmstxt.js +93 -0
- package/dist/commands/llmstxt.test.d.ts +2 -0
- package/dist/commands/plugin.d.ts +24 -0
- package/dist/commands/plugin.js +78 -0
- package/dist/commands/plugin.test.d.ts +2 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.js +8 -0
- package/dist/index.js +33 -4
- package/dist/lib/plugin.d.ts +39 -0
- package/dist/lib/plugin.js +228 -0
- package/dist/lib/plugin.test.d.ts +2 -0
- package/dist/types/index.d.ts +42 -0
- package/dist/ui/App.d.ts +2 -1
- package/dist/ui/App.js +2 -1
- package/dist/ui/LlmsTxt.d.ts +8 -0
- package/dist/ui/LlmsTxt.js +48 -0
- package/dist/ui/Plugin.d.ts +9 -0
- package/dist/ui/Plugin.js +96 -0
- package/modules/obsidian-brain/README.md +32 -0
- package/modules/obsidian-brain/core/templates/braindump.md +15 -0
- package/modules/obsidian-brain/core/templates/consolidation.md +42 -0
- package/modules/obsidian-brain/core/templates/daily-note.md +18 -0
- package/modules/obsidian-brain/core/templates/resource-capture.md +14 -0
- package/modules/obsidian-brain/developer/templates/adr.md +40 -0
- package/modules/obsidian-brain/developer/templates/coding-session.md +24 -0
- package/modules/obsidian-brain/developer/templates/debug-journal.md +22 -0
- package/modules/obsidian-brain/developer/templates/sdd-feedback.md +27 -0
- package/modules/obsidian-brain/developer/templates/tech-debt.md +20 -0
- package/modules/obsidian-brain/pm-lead/templates/daily-brief.md +25 -0
- package/modules/obsidian-brain/pm-lead/templates/meeting-notes.md +24 -0
- package/modules/obsidian-brain/pm-lead/templates/risk-registry.md +12 -0
- package/modules/obsidian-brain/pm-lead/templates/sprint-review.md +27 -0
- package/modules/obsidian-brain/pm-lead/templates/stakeholder-update.md +24 -0
- package/modules/obsidian-brain/pm-lead/templates/team-intelligence.md +19 -0
- package/modules/obsidian-brain/pm-lead/templates/weekly-brief.md +29 -0
- package/package.json +1 -1
- package/schemas/plugin.schema.json +62 -0
- package/ai-config/skills/docs/api-documentation/SKILL.md +0 -293
- package/ai-config/skills/docs/docs-spring/SKILL.md +0 -377
- package/ai-config/skills/docs/mustache-templates/SKILL.md +0 -190
- package/ai-config/skills/docs/technical-docs/SKILL.md +0 -447
- package/dist/commands/analyze.d.ts.map +0 -1
- package/dist/commands/analyze.js.map +0 -1
- package/dist/commands/analyze.test.d.ts.map +0 -1
- package/dist/commands/analyze.test.js +0 -145
- package/dist/commands/analyze.test.js.map +0 -1
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/doctor.test.d.ts.map +0 -1
- package/dist/commands/doctor.test.js +0 -200
- package/dist/commands/doctor.test.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/init.test.d.ts.map +0 -1
- package/dist/commands/init.test.js +0 -271
- package/dist/commands/init.test.js.map +0 -1
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js.map +0 -1
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js.map +0 -1
- package/dist/e2e/aggressive.e2e.test.d.ts.map +0 -1
- package/dist/e2e/aggressive.e2e.test.js +0 -350
- package/dist/e2e/aggressive.e2e.test.js.map +0 -1
- package/dist/e2e/commands.e2e.test.d.ts.map +0 -1
- package/dist/e2e/commands.e2e.test.js +0 -213
- package/dist/e2e/commands.e2e.test.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/common.d.ts.map +0 -1
- package/dist/lib/common.js.map +0 -1
- package/dist/lib/common.test.d.ts.map +0 -1
- package/dist/lib/common.test.js +0 -316
- package/dist/lib/common.test.js.map +0 -1
- package/dist/lib/frontmatter.d.ts.map +0 -1
- package/dist/lib/frontmatter.js.map +0 -1
- package/dist/lib/frontmatter.test.d.ts.map +0 -1
- package/dist/lib/frontmatter.test.js +0 -257
- package/dist/lib/frontmatter.test.js.map +0 -1
- package/dist/lib/template.d.ts.map +0 -1
- package/dist/lib/template.js.map +0 -1
- package/dist/lib/template.test.d.ts.map +0 -1
- package/dist/lib/template.test.js +0 -201
- package/dist/lib/template.test.js.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/ui/AnalyzeUI.d.ts.map +0 -1
- package/dist/ui/AnalyzeUI.js.map +0 -1
- package/dist/ui/App.d.ts.map +0 -1
- package/dist/ui/App.js.map +0 -1
- package/dist/ui/CIContext.d.ts.map +0 -1
- package/dist/ui/CIContext.js.map +0 -1
- package/dist/ui/CISelector.d.ts.map +0 -1
- package/dist/ui/CISelector.js.map +0 -1
- package/dist/ui/Doctor.d.ts.map +0 -1
- package/dist/ui/Doctor.js.map +0 -1
- package/dist/ui/Header.d.ts.map +0 -1
- package/dist/ui/Header.js.map +0 -1
- package/dist/ui/MemorySelector.d.ts.map +0 -1
- package/dist/ui/MemorySelector.js.map +0 -1
- package/dist/ui/NameInput.d.ts.map +0 -1
- package/dist/ui/NameInput.js.map +0 -1
- package/dist/ui/OptionSelector.d.ts.map +0 -1
- package/dist/ui/OptionSelector.js.map +0 -1
- package/dist/ui/Progress.d.ts.map +0 -1
- package/dist/ui/Progress.js.map +0 -1
- package/dist/ui/StackSelector.d.ts.map +0 -1
- package/dist/ui/StackSelector.js.map +0 -1
- package/dist/ui/Summary.d.ts.map +0 -1
- package/dist/ui/Summary.js.map +0 -1
- package/dist/ui/SyncUI.d.ts.map +0 -1
- package/dist/ui/SyncUI.js.map +0 -1
- package/dist/ui/Welcome.d.ts.map +0 -1
- package/dist/ui/Welcome.js.map +0 -1
- package/dist/ui/theme.d.ts.map +0 -1
- package/dist/ui/theme.js.map +0 -1
- package/modules/obsidian-brain/.obsidian/plugins/dataview/data.json +0 -25
- package/modules/obsidian-brain/.obsidian/plugins/obsidian-kanban/data.json +0 -29
- package/modules/obsidian-brain/.obsidian/plugins/templater-obsidian/data.json +0 -18
- package/src/commands/analyze.test.ts +0 -145
- package/src/commands/analyze.ts +0 -69
- package/src/commands/doctor.test.ts +0 -208
- package/src/commands/doctor.ts +0 -163
- package/src/commands/init.test.ts +0 -298
- package/src/commands/init.ts +0 -285
- package/src/constants.ts +0 -69
- package/src/e2e/aggressive.e2e.test.ts +0 -557
- package/src/e2e/commands.e2e.test.ts +0 -298
- package/src/index.tsx +0 -106
- package/src/lib/common.test.ts +0 -318
- package/src/lib/common.ts +0 -127
- package/src/lib/frontmatter.test.ts +0 -291
- package/src/lib/frontmatter.ts +0 -77
- package/src/lib/template.test.ts +0 -226
- package/src/lib/template.ts +0 -99
- package/src/types/index.ts +0 -53
- package/src/ui/AnalyzeUI.tsx +0 -133
- package/src/ui/App.tsx +0 -175
- package/src/ui/CIContext.tsx +0 -25
- package/src/ui/CISelector.tsx +0 -72
- package/src/ui/Doctor.tsx +0 -122
- package/src/ui/Header.tsx +0 -48
- package/src/ui/MemorySelector.tsx +0 -73
- package/src/ui/NameInput.tsx +0 -82
- package/src/ui/OptionSelector.tsx +0 -100
- package/src/ui/Progress.tsx +0 -88
- package/src/ui/StackSelector.tsx +0 -101
- package/src/ui/Summary.tsx +0 -134
- package/src/ui/Welcome.tsx +0 -54
- package/src/ui/theme.ts +0 -10
- package/stryker.config.json +0 -19
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -16
package/.releaserc
CHANGED
|
@@ -35,8 +35,9 @@
|
|
|
35
35
|
["@semantic-release/exec", {
|
|
36
36
|
"prepareCmd": "echo ${nextRelease.version} > .framework-version"
|
|
37
37
|
}],
|
|
38
|
+
"@semantic-release/npm",
|
|
38
39
|
["@semantic-release/git", {
|
|
39
|
-
"assets": ["CHANGELOG.md", ".framework-version"],
|
|
40
|
+
"assets": ["CHANGELOG.md", "package.json", ".framework-version"],
|
|
40
41
|
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
41
42
|
}],
|
|
42
43
|
"@semantic-release/github"
|
package/README.md
CHANGED
|
@@ -1,45 +1,157 @@
|
|
|
1
1
|
# javi-forge
|
|
2
2
|
|
|
3
|
-
Project scaffolding
|
|
3
|
+
> Project scaffolding — AI-ready CI bootstrap with templates for Go, Java, Node, Python, Rust
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/javi-forge)
|
|
6
|
+
[](LICENSE)
|
|
6
7
|
|
|
7
|
-
##
|
|
8
|
+
## Quick Start
|
|
8
9
|
|
|
10
|
+
```bash
|
|
11
|
+
npx javi-forge init
|
|
9
12
|
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
│ ├── skills/ # Skill definitions (by domain)
|
|
29
|
-
│ ├── commands/ # Slash-command definitions
|
|
30
|
-
│ ├── hooks/ # Pre/post tool-use hooks
|
|
31
|
-
│ └── prompts/ # System prompts and modes
|
|
32
|
-
├── schemas/ # JSON schemas for validation
|
|
33
|
-
├── tasks/ # Task templates
|
|
34
|
-
└── src/ # CLI source code
|
|
13
|
+
|
|
14
|
+
An interactive TUI guides you through project setup: pick a stack, CI provider, memory module, and get a production-ready project structure in seconds.
|
|
15
|
+
|
|
16
|
+
## What It Creates
|
|
17
|
+
|
|
18
|
+
`javi-forge init` bootstraps a complete project with 10 sequential steps:
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
flowchart LR
|
|
22
|
+
A["javi-forge init"] --> B["git init"]
|
|
23
|
+
B --> C["CI workflow"]
|
|
24
|
+
C --> D[".gitignore"]
|
|
25
|
+
D --> E["dependabot.yml"]
|
|
26
|
+
E --> F["Memory module"]
|
|
27
|
+
F --> G["javi-ai sync"]
|
|
28
|
+
G --> H["openspec/ (SDD)"]
|
|
29
|
+
H --> I["GHAGGA review"]
|
|
30
|
+
I --> J["Manifest"]
|
|
35
31
|
```
|
|
36
32
|
|
|
37
|
-
|
|
33
|
+
### Step-by-Step
|
|
34
|
+
|
|
35
|
+
| Step | What it does |
|
|
36
|
+
|------|-------------|
|
|
37
|
+
| 1 | Initialize git repository |
|
|
38
|
+
| 2 | Configure git hooks path (`ci-local/hooks`) |
|
|
39
|
+
| 3 | Copy CI workflow for your stack and provider |
|
|
40
|
+
| 4 | Generate `.gitignore` from template |
|
|
41
|
+
| 5 | Generate `dependabot.yml` (GitHub only) |
|
|
42
|
+
| 6 | Install memory module (engram, obsidian-brain, or memory-simple) |
|
|
43
|
+
| 7 | Sync AI config via `javi-ai sync --target all` |
|
|
44
|
+
| 8 | Set up SDD with `openspec/` directory |
|
|
45
|
+
| 9 | Install GHAGGA review system (optional) |
|
|
46
|
+
| 10 | Write forge manifest to `.javi-forge/manifest.json` |
|
|
47
|
+
|
|
48
|
+
## Templates
|
|
49
|
+
|
|
50
|
+
| Stack | CI Templates | Dependabot |
|
|
51
|
+
|-------|-------------|------------|
|
|
52
|
+
| **node** | GitHub Actions, GitLab CI, Woodpecker | npm |
|
|
53
|
+
| **python** | GitHub Actions, GitLab CI, Woodpecker | pip |
|
|
54
|
+
| **go** | GitHub Actions, GitLab CI, Woodpecker | gomod |
|
|
55
|
+
| **java-gradle** | GitHub Actions, GitLab CI, Woodpecker | gradle |
|
|
56
|
+
| **java-maven** | GitHub Actions, GitLab CI, Woodpecker | maven |
|
|
57
|
+
| **rust** | GitHub Actions, GitLab CI, Woodpecker | cargo |
|
|
58
|
+
| **elixir** | GitHub Actions, GitLab CI, Woodpecker | — |
|
|
59
|
+
|
|
60
|
+
## CI Providers
|
|
61
|
+
|
|
62
|
+
| Provider | Workflow location | Dependabot |
|
|
63
|
+
|----------|------------------|------------|
|
|
64
|
+
| **GitHub Actions** | `.github/workflows/ci.yml` | `.github/dependabot.yml` |
|
|
65
|
+
| **GitLab CI** | `.gitlab-ci.yml` | — |
|
|
66
|
+
| **Woodpecker** | `.woodpecker.yml` | — |
|
|
67
|
+
|
|
68
|
+
## Memory Modules
|
|
69
|
+
|
|
70
|
+
| Module | Description |
|
|
71
|
+
|--------|-------------|
|
|
72
|
+
| **engram** | Persistent AI memory via MCP server. Best for cross-session context |
|
|
73
|
+
| **obsidian-brain** | Obsidian-based project memory with Kanban, Dataview, and session tracking |
|
|
74
|
+
| **memory-simple** | Minimal file-based project memory |
|
|
75
|
+
| **none** | Skip memory module |
|
|
76
|
+
|
|
77
|
+
## Commands
|
|
78
|
+
|
|
79
|
+
| Command | Description |
|
|
80
|
+
|---------|-------------|
|
|
81
|
+
| `init` | Bootstrap a new project (default) |
|
|
82
|
+
| `analyze` | Run repoforge skills analysis on current project |
|
|
83
|
+
| `doctor` | Show comprehensive health report |
|
|
38
84
|
|
|
39
85
|
```bash
|
|
40
|
-
npx javi-forge init
|
|
86
|
+
npx javi-forge init
|
|
87
|
+
npx javi-forge init --stack node --ci github
|
|
88
|
+
npx javi-forge analyze
|
|
89
|
+
npx javi-forge doctor
|
|
41
90
|
```
|
|
42
91
|
|
|
92
|
+
### CLI Flags
|
|
93
|
+
|
|
94
|
+
| Flag | Type | Default | Description |
|
|
95
|
+
|------|------|---------|-------------|
|
|
96
|
+
| `--dry-run` | boolean | `false` | Preview without writing files |
|
|
97
|
+
| `--stack` | string | — | Project stack |
|
|
98
|
+
| `--ci` | string | — | CI provider |
|
|
99
|
+
| `--memory` | string | — | Memory module |
|
|
100
|
+
| `--project-name` | string | — | Project name (skips name prompt) |
|
|
101
|
+
| `--ghagga` | boolean | `false` | Enable GHAGGA review system |
|
|
102
|
+
| `--batch` | boolean | `false` | Non-interactive mode |
|
|
103
|
+
|
|
104
|
+
## AI Config
|
|
105
|
+
|
|
106
|
+
`javi-forge` ships with a comprehensive `.ai-config/` library:
|
|
107
|
+
|
|
108
|
+
| Category | Count | Description |
|
|
109
|
+
|----------|-------|-------------|
|
|
110
|
+
| **Agents** | 8 groups | Domain-specific agent definitions |
|
|
111
|
+
| **Skills** | 84 skills | Organized by domain (backend, frontend, infra, etc.) |
|
|
112
|
+
| **Commands** | 20 | Slash-command definitions for Claude |
|
|
113
|
+
| **Hooks** | 11 | Pre/post tool-use automation hooks |
|
|
114
|
+
|
|
115
|
+
The AI config is synced into your project via `javi-ai sync` during init.
|
|
116
|
+
|
|
117
|
+
## RepoForge Integration
|
|
118
|
+
|
|
119
|
+
The `analyze` command wraps [repoforge](https://github.com/Gentleman-Programming/repoforge) to run skills analysis on your project:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npx javi-forge analyze
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
This requires `repoforge` to be installed (`pip install repoforge`). It analyzes your codebase and generates skill recommendations.
|
|
126
|
+
|
|
127
|
+
## Doctor
|
|
128
|
+
|
|
129
|
+
The `doctor` command runs comprehensive health checks:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npx javi-forge doctor
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### What it checks
|
|
136
|
+
|
|
137
|
+
- **System Tools** — git, docker, semgrep, node, pnpm
|
|
138
|
+
- **Framework Structure** — templates/, modules/, ai-config/, workflows/, schemas/, ci-local/
|
|
139
|
+
- **Stack Detection** — Detects project type from files (package.json, go.mod, Cargo.toml, etc.)
|
|
140
|
+
- **Project Manifest** — Reads `.javi-forge/manifest.json`
|
|
141
|
+
- **Installed Modules** — engram, obsidian-brain, memory-simple, ghagga
|
|
142
|
+
|
|
143
|
+
## Requirements
|
|
144
|
+
|
|
145
|
+
- **Node.js** >= 18
|
|
146
|
+
|
|
147
|
+
## Ecosystem
|
|
148
|
+
|
|
149
|
+
| Package | Description |
|
|
150
|
+
|---------|-------------|
|
|
151
|
+
| [javi-dots](https://github.com/JNZader/javi-dots) | Workstation setup (top-level orchestrator) |
|
|
152
|
+
| [javi-ai](https://github.com/JNZader/javi-ai) | AI development layer (called by forge during sync) |
|
|
153
|
+
| **javi-forge** | Project scaffolding (this package) |
|
|
154
|
+
|
|
43
155
|
## License
|
|
44
156
|
|
|
45
|
-
MIT
|
|
157
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: diagnose
|
|
3
|
+
description: Chained diagnosis workflow — investigate issue, verify with devil's advocate, then solve. Uses ACH (Analysis of Competing Hypotheses).
|
|
4
|
+
category: workflows
|
|
5
|
+
chain:
|
|
6
|
+
- investigate
|
|
7
|
+
- verify
|
|
8
|
+
- solve
|
|
9
|
+
suggests_next:
|
|
10
|
+
- /workflows:compound
|
|
11
|
+
- /workflows:review
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# /workflows:diagnose
|
|
15
|
+
|
|
16
|
+
Structured diagnosis workflow for complex bugs and issues.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
/workflows:diagnose <issue-description>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Chain Steps
|
|
25
|
+
|
|
26
|
+
### Step 1: Investigate
|
|
27
|
+
Gather evidence and generate hypotheses using the debug-mode skill.
|
|
28
|
+
|
|
29
|
+
1. Reproduce the issue (or understand the reproduction steps)
|
|
30
|
+
2. Gather context: error messages, stack traces, recent changes, logs
|
|
31
|
+
3. Generate 3-5 competing hypotheses ranked by likelihood
|
|
32
|
+
4. Map evidence to hypotheses (which evidence supports/contradicts each)
|
|
33
|
+
|
|
34
|
+
Output: ACH matrix
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
## ACH Matrix: <issue>
|
|
38
|
+
| Evidence | H1 (70%) | H2 (20%) | H3 (10%) |
|
|
39
|
+
|----------|----------|----------|----------|
|
|
40
|
+
| Error at line 42 | Consistent | Inconsistent | Neutral |
|
|
41
|
+
| Works in staging | Inconsistent | Consistent | Consistent |
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**After completion, suggest**: "Investigation complete. Run step 2 for devil's advocate verification, or jump to step 3 if confident."
|
|
45
|
+
|
|
46
|
+
### Step 2: Verify (Devil's Advocate)
|
|
47
|
+
Challenge the leading hypothesis:
|
|
48
|
+
|
|
49
|
+
1. **Assume H1 is wrong** — what evidence would you expect to see?
|
|
50
|
+
2. **Check for that evidence** — does it exist?
|
|
51
|
+
3. **Try to reproduce with H1 fixed** — does the fix actually work?
|
|
52
|
+
4. If H1 survives the challenge, proceed to fix. Otherwise, promote H2.
|
|
53
|
+
|
|
54
|
+
**After completion, suggest**: "Verification complete. H[N] confirmed. Run step 3 to implement the fix."
|
|
55
|
+
|
|
56
|
+
### Step 3: Solve
|
|
57
|
+
Implement the fix based on the verified hypothesis:
|
|
58
|
+
|
|
59
|
+
1. Write the fix
|
|
60
|
+
2. Verify the original reproduction steps pass
|
|
61
|
+
3. Run the full test suite
|
|
62
|
+
4. Document what was wrong and why (for Engram)
|
|
63
|
+
|
|
64
|
+
**After completion, suggest**: "Fix applied. Next: `/workflows:compound` to capture learnings, or `/workflows:review` for code review."
|
|
65
|
+
|
|
66
|
+
## Rules
|
|
67
|
+
|
|
68
|
+
1. Never skip step 2 for Medium+ complexity issues
|
|
69
|
+
2. Always document the root cause in Engram
|
|
70
|
+
3. If 3 rounds of investigation don't converge, escalate to user
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: discover
|
|
3
|
+
description: Chained discovery workflow — brainstorm ideas, identify assumptions, prioritize experiments, then execute. Progressive suggestions after each step.
|
|
4
|
+
category: workflows
|
|
5
|
+
chain:
|
|
6
|
+
- brainstorm
|
|
7
|
+
- identify-assumptions
|
|
8
|
+
- prioritize
|
|
9
|
+
- experiment
|
|
10
|
+
suggests_next:
|
|
11
|
+
- /workflows:plan
|
|
12
|
+
- /workflows:work
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# /workflows:discover
|
|
16
|
+
|
|
17
|
+
End-to-end discovery workflow that chains 4 steps with progressive suggestions.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
/workflows:discover <topic>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Chain Steps
|
|
26
|
+
|
|
27
|
+
### Step 1: Brainstorm
|
|
28
|
+
Generate 5-10 ideas related to the topic. No filtering, quantity over quality.
|
|
29
|
+
|
|
30
|
+
Output format:
|
|
31
|
+
```
|
|
32
|
+
## Ideas for: <topic>
|
|
33
|
+
1. [idea] — [one-line rationale]
|
|
34
|
+
2. [idea] — [one-line rationale]
|
|
35
|
+
...
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**After completion, suggest**: "Ideas generated. Run step 2 to identify assumptions, or pick specific ideas to explore."
|
|
39
|
+
|
|
40
|
+
### Step 2: Identify Assumptions
|
|
41
|
+
For the top 3-5 ideas, list the key assumptions that must be true for each to work.
|
|
42
|
+
|
|
43
|
+
Output format:
|
|
44
|
+
```
|
|
45
|
+
## Assumptions
|
|
46
|
+
### Idea 1: [name]
|
|
47
|
+
- [assumption] — risk: high/medium/low
|
|
48
|
+
- [assumption] — risk: high/medium/low
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**After completion, suggest**: "Assumptions mapped. Run step 3 to prioritize by risk, or go back to brainstorm more."
|
|
52
|
+
|
|
53
|
+
### Step 3: Prioritize
|
|
54
|
+
Rank ideas by: (1) impact, (2) effort, (3) assumption risk. Create a 2x2 matrix.
|
|
55
|
+
|
|
56
|
+
Output format:
|
|
57
|
+
```
|
|
58
|
+
## Priority Matrix
|
|
59
|
+
| Idea | Impact | Effort | Risk | Priority |
|
|
60
|
+
|------|--------|--------|------|----------|
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**After completion, suggest**: "Priority matrix ready. Run step 4 to design experiments for the top picks."
|
|
64
|
+
|
|
65
|
+
### Step 4: Experiment Design
|
|
66
|
+
For the top 2-3 ideas, design a minimal experiment to validate the riskiest assumption.
|
|
67
|
+
|
|
68
|
+
Output format:
|
|
69
|
+
```
|
|
70
|
+
## Experiments
|
|
71
|
+
### Experiment 1: [name]
|
|
72
|
+
- Validates: [assumption]
|
|
73
|
+
- Method: [how to test]
|
|
74
|
+
- Success criteria: [what proves it works]
|
|
75
|
+
- Effort: [time estimate]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**After completion, suggest**: "Experiments designed. Next steps: `/workflows:plan` to plan implementation, or `/workflows:work` to start building."
|
|
79
|
+
|
|
80
|
+
## Chaining Rules
|
|
81
|
+
|
|
82
|
+
1. Each step can be run independently or as part of the chain
|
|
83
|
+
2. Output of each step is input context for the next
|
|
84
|
+
3. Progressive suggestions appear after every step completion
|
|
85
|
+
4. User can skip steps or go back at any point
|
|
86
|
+
5. The full chain takes ~15 minutes of agent time
|
package/dist/commands/doctor.js
CHANGED
|
@@ -3,7 +3,8 @@ import path from 'path';
|
|
|
3
3
|
import { execFile } from 'child_process';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import { detectStack } from '../lib/common.js';
|
|
6
|
-
import {
|
|
6
|
+
import { listInstalledPlugins } from '../lib/plugin.js';
|
|
7
|
+
import { FORGE_ROOT, TEMPLATES_DIR, MODULES_DIR, AI_CONFIG_DIR, PLUGINS_DIR } from '../constants.js';
|
|
7
8
|
const execFileAsync = promisify(execFile);
|
|
8
9
|
/** Resolve a binary name to its full path, returns null if not found */
|
|
9
10
|
async function which(bin) {
|
|
@@ -153,6 +154,28 @@ export async function runDoctor(projectDir) {
|
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
sections.push({ title: 'Installed Modules', checks: moduleChecks });
|
|
157
|
+
// ── 6. Plugins ─────────────────────────────────────────────────────────────
|
|
158
|
+
const pluginChecks = [];
|
|
159
|
+
const pluginsDirExists = await fs.pathExists(PLUGINS_DIR);
|
|
160
|
+
if (pluginsDirExists) {
|
|
161
|
+
const plugins = await listInstalledPlugins();
|
|
162
|
+
if (plugins.length > 0) {
|
|
163
|
+
for (const plugin of plugins) {
|
|
164
|
+
pluginChecks.push({
|
|
165
|
+
label: plugin.name,
|
|
166
|
+
status: 'ok',
|
|
167
|
+
detail: `v${plugin.version} from ${plugin.source}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
pluginChecks.push({ label: 'Plugins', status: 'skip', detail: 'none installed' });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
pluginChecks.push({ label: 'Plugins directory', status: 'skip', detail: 'not created yet' });
|
|
177
|
+
}
|
|
178
|
+
sections.push({ title: 'Plugins', checks: pluginChecks });
|
|
156
179
|
return { sections };
|
|
157
180
|
}
|
|
158
181
|
//# sourceMappingURL=doctor.js.map
|
package/dist/commands/init.js
CHANGED
|
@@ -250,7 +250,54 @@ export async function initProject(options, onStep) {
|
|
|
250
250
|
catch (e) {
|
|
251
251
|
report(onStep, stepGhagga, 'Install GHAGGA review system', 'error', String(e));
|
|
252
252
|
}
|
|
253
|
-
// ── Step 10:
|
|
253
|
+
// ── Step 10: Mock-first mode ───────────────────────────────────────────────
|
|
254
|
+
const stepMock = 'mock';
|
|
255
|
+
if (options.mock) {
|
|
256
|
+
report(onStep, stepMock, 'Configure mock-first mode', 'running');
|
|
257
|
+
try {
|
|
258
|
+
if (!dryRun) {
|
|
259
|
+
// Create .env.example with mock values
|
|
260
|
+
const envExample = `# Mock environment — no real API keys required
|
|
261
|
+
# Copy to .env to use: cp .env.example .env
|
|
262
|
+
|
|
263
|
+
# Database
|
|
264
|
+
DATABASE_URL=postgresql://mock:mock@localhost:5432/mock_db
|
|
265
|
+
|
|
266
|
+
# Auth
|
|
267
|
+
JWT_SECRET=mock-jwt-secret-for-local-development
|
|
268
|
+
SESSION_SECRET=mock-session-secret
|
|
269
|
+
|
|
270
|
+
# External APIs (mock mode — no real calls)
|
|
271
|
+
MOCK_MODE=true
|
|
272
|
+
API_KEY=mock-api-key-not-real
|
|
273
|
+
STRIPE_KEY=sk_test_mock_not_real
|
|
274
|
+
SENDGRID_KEY=SG.mock_not_real
|
|
275
|
+
|
|
276
|
+
# Feature flags
|
|
277
|
+
ENABLE_ANALYTICS=false
|
|
278
|
+
ENABLE_EMAILS=false
|
|
279
|
+
ENABLE_WEBHOOKS=false
|
|
280
|
+
`;
|
|
281
|
+
const envExamplePath = path.join(projectDir, '.env.example');
|
|
282
|
+
if (!await fs.pathExists(envExamplePath)) {
|
|
283
|
+
await fs.writeFile(envExamplePath, envExample, 'utf-8');
|
|
284
|
+
}
|
|
285
|
+
// Create .env from example
|
|
286
|
+
const envPath = path.join(projectDir, '.env');
|
|
287
|
+
if (!await fs.pathExists(envPath)) {
|
|
288
|
+
await fs.writeFile(envPath, envExample, 'utf-8');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
report(onStep, stepMock, 'Configure mock-first mode', 'done', '.env.example + .env with mock values');
|
|
292
|
+
}
|
|
293
|
+
catch (e) {
|
|
294
|
+
report(onStep, stepMock, 'Configure mock-first mode', 'error', String(e));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
report(onStep, stepMock, 'Configure mock-first mode', 'skipped', 'not selected');
|
|
299
|
+
}
|
|
300
|
+
// ── Step 11: Write manifest ───────────────────────────────────────────────
|
|
254
301
|
const stepManifest = 'manifest';
|
|
255
302
|
report(onStep, stepManifest, 'Write forge manifest', 'running');
|
|
256
303
|
try {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InitStep } from '../types/index.js';
|
|
2
|
+
type StepCallback = (step: InitStep) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Generate an llms.txt file — compact AI-friendly project notation.
|
|
5
|
+
* Reduces token usage by ~75% compared to full README + docs.
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateLlmsTxt(projectDir: string, dryRun: boolean, onStep: StepCallback): Promise<void>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=llmstxt.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
import { detectStack } from '../lib/common.js';
|
|
5
|
+
function report(onStep, id, label, status, detail) {
|
|
6
|
+
onStep({ id, label, status, detail });
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Generate an llms.txt file — compact AI-friendly project notation.
|
|
10
|
+
* Reduces token usage by ~75% compared to full README + docs.
|
|
11
|
+
*/
|
|
12
|
+
export async function generateLlmsTxt(projectDir, dryRun, onStep) {
|
|
13
|
+
report(onStep, 'scan', 'Scan project structure', 'running');
|
|
14
|
+
const detection = await detectStack(projectDir);
|
|
15
|
+
const stackLabel = detection?.stackType ?? 'unknown';
|
|
16
|
+
// Gather project info
|
|
17
|
+
const name = path.basename(projectDir);
|
|
18
|
+
let description = '';
|
|
19
|
+
let version = '';
|
|
20
|
+
let entryPoints = [];
|
|
21
|
+
let dependencies = [];
|
|
22
|
+
// Try package.json (Node)
|
|
23
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
24
|
+
if (await fs.pathExists(pkgPath)) {
|
|
25
|
+
try {
|
|
26
|
+
const pkg = await fs.readJson(pkgPath);
|
|
27
|
+
description = pkg.description ?? '';
|
|
28
|
+
version = pkg.version ?? '';
|
|
29
|
+
entryPoints = pkg.main ? [pkg.main] : [];
|
|
30
|
+
dependencies = Object.keys(pkg.dependencies ?? {}).slice(0, 15);
|
|
31
|
+
}
|
|
32
|
+
catch { /* ignore */ }
|
|
33
|
+
}
|
|
34
|
+
// Scan source files
|
|
35
|
+
const sourceFiles = await glob('src/**/*.{ts,tsx,js,jsx,py,go,rs}', {
|
|
36
|
+
cwd: projectDir,
|
|
37
|
+
ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**'],
|
|
38
|
+
});
|
|
39
|
+
// Scan test files
|
|
40
|
+
const testFiles = await glob('**/*.{test,spec}.{ts,tsx,js,jsx,py}', {
|
|
41
|
+
cwd: projectDir,
|
|
42
|
+
ignore: ['**/node_modules/**', '**/dist/**'],
|
|
43
|
+
});
|
|
44
|
+
report(onStep, 'scan', 'Scan project structure', 'done', `${sourceFiles.length} source, ${testFiles.length} test files`);
|
|
45
|
+
// Build llms.txt content
|
|
46
|
+
report(onStep, 'generate', 'Generate llms.txt', 'running');
|
|
47
|
+
const lines = [
|
|
48
|
+
`# ${name}`,
|
|
49
|
+
'',
|
|
50
|
+
`> ${description || 'No description'}`,
|
|
51
|
+
'',
|
|
52
|
+
`- stack: ${stackLabel}${detection?.buildTool ? ` (${detection.buildTool})` : ''}`,
|
|
53
|
+
`- version: ${version || 'unknown'}`,
|
|
54
|
+
`- files: ${sourceFiles.length} source, ${testFiles.length} tests`,
|
|
55
|
+
'',
|
|
56
|
+
];
|
|
57
|
+
// Structure summary (compact)
|
|
58
|
+
if (sourceFiles.length > 0) {
|
|
59
|
+
lines.push('## Structure');
|
|
60
|
+
const dirs = new Map();
|
|
61
|
+
for (const f of sourceFiles) {
|
|
62
|
+
const dir = path.dirname(f);
|
|
63
|
+
dirs.set(dir, (dirs.get(dir) ?? 0) + 1);
|
|
64
|
+
}
|
|
65
|
+
for (const [dir, count] of [...dirs.entries()].sort().slice(0, 20)) {
|
|
66
|
+
lines.push(`- ${dir}/ (${count})`);
|
|
67
|
+
}
|
|
68
|
+
lines.push('');
|
|
69
|
+
}
|
|
70
|
+
// Dependencies (compact — name only, no versions)
|
|
71
|
+
if (dependencies.length > 0) {
|
|
72
|
+
lines.push('## Dependencies');
|
|
73
|
+
lines.push(dependencies.join(', '));
|
|
74
|
+
lines.push('');
|
|
75
|
+
}
|
|
76
|
+
// Entry points
|
|
77
|
+
if (entryPoints.length > 0) {
|
|
78
|
+
lines.push('## Entry');
|
|
79
|
+
for (const ep of entryPoints) {
|
|
80
|
+
lines.push(`- ${ep}`);
|
|
81
|
+
}
|
|
82
|
+
lines.push('');
|
|
83
|
+
}
|
|
84
|
+
const content = lines.join('\n');
|
|
85
|
+
const tokenEstimate = Math.ceil(content.length / 4);
|
|
86
|
+
if (!dryRun) {
|
|
87
|
+
await fs.writeFile(path.join(projectDir, 'llms.txt'), content, 'utf-8');
|
|
88
|
+
}
|
|
89
|
+
report(onStep, 'generate', 'Generate llms.txt', 'done', dryRun
|
|
90
|
+
? `dry-run: ~${tokenEstimate} tokens (${content.length} chars)`
|
|
91
|
+
: `written — ~${tokenEstimate} tokens (${content.length} chars)`);
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=llmstxt.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { InitStep } from '../types/index.js';
|
|
2
|
+
type StepCallback = (step: InitStep) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Add (install) a plugin from a GitHub source.
|
|
5
|
+
*/
|
|
6
|
+
export declare function runPluginAdd(source: string, dryRun: boolean, onStep: StepCallback): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Remove an installed plugin by name.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runPluginRemove(name: string, dryRun: boolean, onStep: StepCallback): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* List all installed plugins.
|
|
13
|
+
*/
|
|
14
|
+
export declare function runPluginList(onStep: StepCallback): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Search the remote plugin registry.
|
|
17
|
+
*/
|
|
18
|
+
export declare function runPluginSearch(query: string | undefined, onStep: StepCallback): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Validate a local plugin directory.
|
|
21
|
+
*/
|
|
22
|
+
export declare function runPluginValidate(pluginDir: string, onStep: StepCallback): Promise<void>;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { installPlugin, removePlugin, listInstalledPlugins, validatePlugin, searchRegistry, } from '../lib/plugin.js';
|
|
2
|
+
function report(onStep, id, label, status, detail) {
|
|
3
|
+
onStep({ id, label, status, detail });
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Add (install) a plugin from a GitHub source.
|
|
7
|
+
*/
|
|
8
|
+
export async function runPluginAdd(source, dryRun, onStep) {
|
|
9
|
+
const stepId = 'plugin-add';
|
|
10
|
+
report(onStep, stepId, `Install plugin: ${source}`, 'running');
|
|
11
|
+
const result = await installPlugin(source, { dryRun });
|
|
12
|
+
if (result.success) {
|
|
13
|
+
report(onStep, stepId, `Install plugin: ${source}`, 'done', dryRun ? `dry-run: would install ${result.name}` : `installed ${result.name}`);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
report(onStep, stepId, `Install plugin: ${source}`, 'error', result.error);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Remove an installed plugin by name.
|
|
21
|
+
*/
|
|
22
|
+
export async function runPluginRemove(name, dryRun, onStep) {
|
|
23
|
+
const stepId = 'plugin-remove';
|
|
24
|
+
report(onStep, stepId, `Remove plugin: ${name}`, 'running');
|
|
25
|
+
const result = await removePlugin(name, { dryRun });
|
|
26
|
+
if (result.success) {
|
|
27
|
+
report(onStep, stepId, `Remove plugin: ${name}`, 'done', dryRun ? `dry-run: would remove ${name}` : `removed ${name}`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
report(onStep, stepId, `Remove plugin: ${name}`, 'error', result.error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* List all installed plugins.
|
|
35
|
+
*/
|
|
36
|
+
export async function runPluginList(onStep) {
|
|
37
|
+
const stepId = 'plugin-list';
|
|
38
|
+
report(onStep, stepId, 'List installed plugins', 'running');
|
|
39
|
+
const plugins = await listInstalledPlugins();
|
|
40
|
+
if (plugins.length === 0) {
|
|
41
|
+
report(onStep, stepId, 'List installed plugins', 'done', 'no plugins installed');
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const summary = plugins.map(p => `${p.name}@${p.version}`).join(', ');
|
|
45
|
+
report(onStep, stepId, 'List installed plugins', 'done', `${plugins.length} plugins: ${summary}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Search the remote plugin registry.
|
|
50
|
+
*/
|
|
51
|
+
export async function runPluginSearch(query, onStep) {
|
|
52
|
+
const stepId = 'plugin-search';
|
|
53
|
+
report(onStep, stepId, `Search plugins${query ? `: ${query}` : ''}`, 'running');
|
|
54
|
+
const results = await searchRegistry(query);
|
|
55
|
+
if (results.length === 0) {
|
|
56
|
+
report(onStep, stepId, `Search plugins${query ? `: ${query}` : ''}`, 'done', query ? `no plugins matching "${query}"` : 'registry empty or unreachable');
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const summary = results.map(p => `${p.id} — ${p.description}`).join('\n ');
|
|
60
|
+
report(onStep, stepId, `Search plugins${query ? `: ${query}` : ''}`, 'done', `${results.length} results:\n ${summary}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate a local plugin directory.
|
|
65
|
+
*/
|
|
66
|
+
export async function runPluginValidate(pluginDir, onStep) {
|
|
67
|
+
const stepId = 'plugin-validate';
|
|
68
|
+
report(onStep, stepId, `Validate plugin: ${pluginDir}`, 'running');
|
|
69
|
+
const result = await validatePlugin(pluginDir);
|
|
70
|
+
if (result.valid) {
|
|
71
|
+
report(onStep, stepId, `Validate plugin: ${pluginDir}`, 'done', `valid — ${result.manifest?.name}@${result.manifest?.version}`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const msgs = result.errors.map(e => ` ${e.path}: ${e.message}`).join('\n');
|
|
75
|
+
report(onStep, stepId, `Validate plugin: ${pluginDir}`, 'error', `${result.errors.length} errors:\n${msgs}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=plugin.js.map
|