@aion0/forge 0.3.5 → 0.3.7
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/CLAUDE.md +15 -0
- package/app/api/help/route.ts +78 -0
- package/app/api/logs/route.ts +100 -0
- package/app/api/settings/route.ts +2 -0
- package/bin/forge-server.mjs +9 -0
- package/components/Dashboard.tsx +31 -1
- package/components/HelpDialog.tsx +169 -0
- package/components/HelpTerminal.tsx +130 -0
- package/components/LogViewer.tsx +194 -0
- package/components/ProjectManager.tsx +26 -8
- package/lib/auth.ts +2 -0
- package/lib/cloudflared.ts +71 -25
- package/lib/help-docs/00-overview.md +34 -0
- package/lib/help-docs/01-settings.md +37 -0
- package/lib/help-docs/02-telegram.md +41 -0
- package/lib/help-docs/03-tunnel.md +31 -0
- package/lib/help-docs/04-tasks.md +52 -0
- package/lib/help-docs/05-pipelines.md +73 -0
- package/lib/help-docs/06-skills.md +43 -0
- package/lib/help-docs/07-projects.md +39 -0
- package/lib/help-docs/08-rules.md +53 -0
- package/lib/help-docs/09-issue-autofix.md +51 -0
- package/lib/help-docs/10-troubleshooting.md +82 -0
- package/lib/init.ts +3 -0
- package/lib/logger.ts +73 -0
- package/lib/password.ts +1 -1
- package/lib/skills.ts +6 -0
- package/lib/task-manager.ts +2 -0
- package/next-env.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Pipelines (Workflows)
|
|
2
|
+
|
|
3
|
+
## What Are Pipelines?
|
|
4
|
+
|
|
5
|
+
Pipelines chain multiple tasks into a DAG (directed acyclic graph). Each step can depend on previous steps, pass outputs forward, and run in parallel.
|
|
6
|
+
|
|
7
|
+
## YAML Workflow Format
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
name: my-workflow
|
|
11
|
+
description: "What this workflow does"
|
|
12
|
+
input:
|
|
13
|
+
feature: "Feature description"
|
|
14
|
+
vars:
|
|
15
|
+
project: my-app
|
|
16
|
+
nodes:
|
|
17
|
+
design:
|
|
18
|
+
project: "{{vars.project}}"
|
|
19
|
+
prompt: "Design: {{input.feature}}"
|
|
20
|
+
outputs:
|
|
21
|
+
- name: spec
|
|
22
|
+
extract: result
|
|
23
|
+
implement:
|
|
24
|
+
project: "{{vars.project}}"
|
|
25
|
+
depends_on: [design]
|
|
26
|
+
prompt: "Implement: {{nodes.design.outputs.spec}}"
|
|
27
|
+
review:
|
|
28
|
+
project: "{{vars.project}}"
|
|
29
|
+
depends_on: [implement]
|
|
30
|
+
prompt: "Review the changes"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Node Options
|
|
34
|
+
|
|
35
|
+
| Field | Description |
|
|
36
|
+
|-------|-------------|
|
|
37
|
+
| `project` | Project name (supports `{{vars.xxx}}` templates) |
|
|
38
|
+
| `prompt` | Claude Code prompt or shell command |
|
|
39
|
+
| `mode` | `claude` (default) or `shell` |
|
|
40
|
+
| `branch` | Auto-checkout branch before running |
|
|
41
|
+
| `depends_on` | List of node IDs that must complete first |
|
|
42
|
+
| `outputs` | Extract results: `result`, `git_diff`, or `stdout` |
|
|
43
|
+
| `routes` | Conditional routing to next nodes |
|
|
44
|
+
|
|
45
|
+
## Template Variables
|
|
46
|
+
|
|
47
|
+
- `{{input.xxx}}` — pipeline input values
|
|
48
|
+
- `{{vars.xxx}}` — workflow variables
|
|
49
|
+
- `{{nodes.xxx.outputs.yyy}}` — outputs from previous nodes
|
|
50
|
+
|
|
51
|
+
## Built-in Workflows
|
|
52
|
+
|
|
53
|
+
### issue-auto-fix
|
|
54
|
+
Fetches a GitHub issue → fixes code on new branch → creates PR.
|
|
55
|
+
|
|
56
|
+
Input: `issue_id`, `project`, `base_branch` (optional)
|
|
57
|
+
|
|
58
|
+
### pr-review
|
|
59
|
+
Fetches PR diff → AI code review → posts result.
|
|
60
|
+
|
|
61
|
+
Input: `pr_number`, `project`
|
|
62
|
+
|
|
63
|
+
## CLI
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
forge flows # list available workflows
|
|
67
|
+
forge run my-workflow # execute a workflow
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Storage
|
|
71
|
+
|
|
72
|
+
- Workflow YAML: `~/.forge/data/flows/`
|
|
73
|
+
- Execution state: `~/.forge/data/pipelines/`
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Skills Marketplace
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Browse, install, and manage Claude Code skills and commands from the Forge Skills registry.
|
|
6
|
+
|
|
7
|
+
## Types
|
|
8
|
+
|
|
9
|
+
| | Skills | Commands |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Location | `~/.claude/skills/<name>/` | `~/.claude/commands/<name>.md` |
|
|
12
|
+
| Entry file | `SKILL.md` | Single `.md` file |
|
|
13
|
+
| Complexity | Multi-file with templates | Simple slash command |
|
|
14
|
+
|
|
15
|
+
Both register as `/slash-command` in Claude Code.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
1. Go to **Skills** tab in Forge
|
|
20
|
+
2. Click **Sync** to fetch latest registry
|
|
21
|
+
3. Click **Install** on any skill → choose Global or specific project
|
|
22
|
+
4. Use in Claude Code with `/<skill-name>`
|
|
23
|
+
|
|
24
|
+
## Update
|
|
25
|
+
|
|
26
|
+
Skills with newer versions show a yellow "update" indicator. Click to update (checks for local modifications first).
|
|
27
|
+
|
|
28
|
+
## Local Skills
|
|
29
|
+
|
|
30
|
+
The **Local** tab shows skills/commands installed on your machine (both from marketplace and manually created). You can:
|
|
31
|
+
- **Install to...** — Copy a local skill to another project or global
|
|
32
|
+
- **Delete** — Remove from project or global
|
|
33
|
+
- **Edit** — View and modify installed files
|
|
34
|
+
|
|
35
|
+
## Registry
|
|
36
|
+
|
|
37
|
+
Default: `https://raw.githubusercontent.com/aiwatching/forge-skills/main`
|
|
38
|
+
|
|
39
|
+
Change in Settings → Skills Repo URL.
|
|
40
|
+
|
|
41
|
+
## Custom Skills
|
|
42
|
+
|
|
43
|
+
Create your own: put a `.md` file in `<project>/.claude/commands/` or a directory in `<project>/.claude/skills/<name>/`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Projects
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
Add project directories in Settings → **Project Roots** (e.g. `~/Projects`). Forge scans subdirectories automatically.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### Code Tab
|
|
10
|
+
- File tree browser
|
|
11
|
+
- Syntax-highlighted code viewer
|
|
12
|
+
- Git diff view (click changed files)
|
|
13
|
+
- Git operations: commit, push, pull
|
|
14
|
+
- Commit history
|
|
15
|
+
|
|
16
|
+
### Skills & Commands Tab
|
|
17
|
+
- View installed skills/commands for this project
|
|
18
|
+
- Scope indicator: G (global), P (project), G+P (both)
|
|
19
|
+
- Edit files, update from marketplace, uninstall
|
|
20
|
+
|
|
21
|
+
### CLAUDE.md Tab
|
|
22
|
+
- View and edit project's CLAUDE.md
|
|
23
|
+
- Apply rule templates (built-in or custom)
|
|
24
|
+
- Templates auto-injected with dedup markers
|
|
25
|
+
|
|
26
|
+
### Issues Tab
|
|
27
|
+
- Enable GitHub Issue Auto-fix per project
|
|
28
|
+
- Configure scan interval and label filters
|
|
29
|
+
- Manual trigger: enter issue # and click Fix Issue
|
|
30
|
+
- Processed issues history with retry/delete
|
|
31
|
+
- Auto-chains: fix → create PR → review
|
|
32
|
+
|
|
33
|
+
## Favorites
|
|
34
|
+
|
|
35
|
+
Click ★ next to a project to favorite it. Favorites appear at the top of the sidebar.
|
|
36
|
+
|
|
37
|
+
## Terminal
|
|
38
|
+
|
|
39
|
+
Click "Terminal" button in project header to open a Vibe Coding terminal for that project. Uses `claude -c` to continue last session.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Rules (CLAUDE.md Templates)
|
|
2
|
+
|
|
3
|
+
## What Are Rules?
|
|
4
|
+
|
|
5
|
+
Reusable markdown snippets that get appended to project CLAUDE.md files. They define coding conventions, security rules, workflow guidelines, etc.
|
|
6
|
+
|
|
7
|
+
## Built-in Templates
|
|
8
|
+
|
|
9
|
+
| Template | Description |
|
|
10
|
+
|----------|-------------|
|
|
11
|
+
| TypeScript Rules | Coding conventions (const, types, early returns) |
|
|
12
|
+
| Git Workflow | Commit messages, branch naming |
|
|
13
|
+
| Obsidian Vault | Vault integration instructions |
|
|
14
|
+
| Security Rules | OWASP guidelines, no hardcoded secrets |
|
|
15
|
+
|
|
16
|
+
## Manage Rules
|
|
17
|
+
|
|
18
|
+
**Skills tab → Rules sub-tab**:
|
|
19
|
+
- View all templates (built-in + custom)
|
|
20
|
+
- Create new: click "+ New"
|
|
21
|
+
- Edit any template (including built-in)
|
|
22
|
+
- Delete custom templates
|
|
23
|
+
- Set as "default" — auto-applied to new projects
|
|
24
|
+
- Batch apply: select template → check projects → click "Apply"
|
|
25
|
+
|
|
26
|
+
## Apply to Project
|
|
27
|
+
|
|
28
|
+
**Project → CLAUDE.md tab**:
|
|
29
|
+
- Left sidebar shows CLAUDE.md content + template list
|
|
30
|
+
- Click "+ add" to inject a template
|
|
31
|
+
- Click "added" to remove
|
|
32
|
+
- Templates wrapped in `<!-- forge:template:id -->` markers (prevents duplicate injection)
|
|
33
|
+
|
|
34
|
+
## Default Templates
|
|
35
|
+
|
|
36
|
+
Templates marked as "default" are automatically injected into new projects when they first appear in the project list.
|
|
37
|
+
|
|
38
|
+
## Custom Templates
|
|
39
|
+
|
|
40
|
+
Stored in `~/.forge/data/claude-templates/`. Each is a `.md` file with YAML frontmatter:
|
|
41
|
+
|
|
42
|
+
```markdown
|
|
43
|
+
---
|
|
44
|
+
name: My Rule
|
|
45
|
+
description: What this rule does
|
|
46
|
+
tags: [category]
|
|
47
|
+
builtin: false
|
|
48
|
+
isDefault: false
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## My Custom Rule
|
|
52
|
+
Your content here...
|
|
53
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Issue Auto-fix
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Automatically scan GitHub Issues, fix code, create PRs, and review — all hands-free.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- `gh` CLI installed and authenticated: `gh auth login`
|
|
10
|
+
- Project has a GitHub remote
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
1. Go to **Projects → select project → Issues tab**
|
|
15
|
+
2. Enable **Issue Auto-fix**
|
|
16
|
+
3. Configure:
|
|
17
|
+
- **Scan Interval**: minutes between scans (0 = manual only)
|
|
18
|
+
- **Base Branch**: leave empty for auto-detect (main/master)
|
|
19
|
+
- **Labels Filter**: comma-separated labels (empty = all issues)
|
|
20
|
+
4. Click **Scan Now** to test
|
|
21
|
+
|
|
22
|
+
## Flow
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Scan → Fetch Issue → Fix Code (new branch) → Push → Create PR → Auto Review → Notify
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
1. **Scan**: `gh issue list` finds open issues matching labels
|
|
29
|
+
2. **Fix**: Claude Code analyzes issue and fixes code on `fix/<id>-<description>` branch
|
|
30
|
+
3. **PR**: Pushes branch and creates Pull Request
|
|
31
|
+
4. **Review**: Automatically triggers `pr-review` pipeline
|
|
32
|
+
5. **Notify**: Results sent via Telegram (if configured)
|
|
33
|
+
|
|
34
|
+
## Manual Trigger
|
|
35
|
+
|
|
36
|
+
Enter an issue number in "Manual Trigger" section and click "Fix Issue".
|
|
37
|
+
|
|
38
|
+
## Retry
|
|
39
|
+
|
|
40
|
+
Failed fixes show a "Retry" button. Click to provide additional context (e.g. "rebase from main first") and re-run.
|
|
41
|
+
|
|
42
|
+
## Safety
|
|
43
|
+
|
|
44
|
+
- Checks for uncommitted changes before starting (aborts if dirty)
|
|
45
|
+
- Always works on new branches (never modifies main)
|
|
46
|
+
- Switches back to original branch after completion
|
|
47
|
+
- Existing PRs are updated, not duplicated
|
|
48
|
+
|
|
49
|
+
## Processed Issues
|
|
50
|
+
|
|
51
|
+
History shows all processed issues with status (processing/done/failed), PR number, and pipeline ID. Click pipeline ID to view details.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## Common Issues
|
|
4
|
+
|
|
5
|
+
### "fork failed: Device not configured" (macOS)
|
|
6
|
+
PTY device limit exhausted:
|
|
7
|
+
```bash
|
|
8
|
+
sudo sysctl kern.tty.ptmx_max=2048
|
|
9
|
+
echo 'kern.tty.ptmx_max=2048' | sudo tee -a /etc/sysctl.conf
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Session cookie invalid after restart
|
|
13
|
+
Fix AUTH_SECRET so it persists:
|
|
14
|
+
```bash
|
|
15
|
+
echo "AUTH_SECRET=$(openssl rand -hex 32)" >> ~/.forge/data/.env.local
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Orphan processes after Ctrl+C
|
|
19
|
+
Use `forge server stop` or:
|
|
20
|
+
```bash
|
|
21
|
+
pkill -f 'telegram-standalone|terminal-standalone|next-server|cloudflared'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Tunnel stuck at "starting"
|
|
25
|
+
```bash
|
|
26
|
+
pkill -f cloudflared
|
|
27
|
+
# Then restart tunnel from UI or Telegram
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Forgot admin password
|
|
31
|
+
```bash
|
|
32
|
+
forge --reset-password
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Terminal tabs lost after restart
|
|
36
|
+
Terminal state is saved in `~/.forge/data/terminal-state.json`. If corrupted:
|
|
37
|
+
```bash
|
|
38
|
+
rm ~/.forge/data/terminal-state.json
|
|
39
|
+
# Restart server — tabs will be empty but tmux sessions survive
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### gh CLI not authenticated (Issue Scanner)
|
|
43
|
+
```bash
|
|
44
|
+
gh auth login
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Skills not syncing
|
|
48
|
+
Click "Sync" in Skills tab. Check `skillsRepoUrl` in Settings points to valid registry.
|
|
49
|
+
|
|
50
|
+
### Multiple instances conflict
|
|
51
|
+
Use different ports and data directories:
|
|
52
|
+
```bash
|
|
53
|
+
forge server start --port 4000 --dir ~/.forge/data_demo
|
|
54
|
+
forge server stop --port 4000 --dir ~/.forge/data_demo
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Page shows "Failed to load chunk"
|
|
58
|
+
Clear build cache:
|
|
59
|
+
```bash
|
|
60
|
+
rm -rf .next
|
|
61
|
+
pnpm build # or forge server rebuild
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Logs
|
|
65
|
+
|
|
66
|
+
- Background server: `~/.forge/data/forge.log`
|
|
67
|
+
- Dev mode: terminal output
|
|
68
|
+
- View with: `tail -f ~/.forge/data/forge.log`
|
|
69
|
+
|
|
70
|
+
## Reset Everything
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Stop server
|
|
74
|
+
forge server stop
|
|
75
|
+
|
|
76
|
+
# Reset password
|
|
77
|
+
forge --reset-password
|
|
78
|
+
|
|
79
|
+
# Clear all data (nuclear option)
|
|
80
|
+
rm -rf ~/.forge/data
|
|
81
|
+
# Restart — will create fresh data directory
|
|
82
|
+
```
|
package/lib/init.ts
CHANGED
|
@@ -64,6 +64,9 @@ export function ensureInitialized() {
|
|
|
64
64
|
if (gInit[initKey]) return;
|
|
65
65
|
gInit[initKey] = true;
|
|
66
66
|
|
|
67
|
+
// Add timestamps to all console output
|
|
68
|
+
try { const { initLogger } = require('./logger'); initLogger(); } catch {}
|
|
69
|
+
|
|
67
70
|
// Migrate old data layout (~/.forge/* → ~/.forge/data/*) on first run
|
|
68
71
|
try { const { migrateDataDir } = require('./dirs'); migrateDataDir(); } catch {}
|
|
69
72
|
|
package/lib/logger.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger — adds timestamps + writes to forge.log file.
|
|
3
|
+
* Call `initLogger()` once at startup.
|
|
4
|
+
* Works in both dev mode (terminal + file) and production (file via redirect).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { appendFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
|
|
10
|
+
let initialized = false;
|
|
11
|
+
|
|
12
|
+
export function initLogger() {
|
|
13
|
+
if (initialized) return;
|
|
14
|
+
initialized = true;
|
|
15
|
+
|
|
16
|
+
// Determine log file path
|
|
17
|
+
let logFile: string | null = null;
|
|
18
|
+
try {
|
|
19
|
+
const { getDataDir } = require('./dirs');
|
|
20
|
+
const dataDir = getDataDir();
|
|
21
|
+
if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });
|
|
22
|
+
logFile = join(dataDir, 'forge.log');
|
|
23
|
+
} catch {}
|
|
24
|
+
|
|
25
|
+
const origLog = console.log;
|
|
26
|
+
const origError = console.error;
|
|
27
|
+
const origWarn = console.warn;
|
|
28
|
+
|
|
29
|
+
const ts = () => new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
30
|
+
|
|
31
|
+
const writeToFile = (line: string) => {
|
|
32
|
+
if (!logFile) return;
|
|
33
|
+
try { appendFileSync(logFile, line + '\n'); } catch {}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const SENSITIVE_PATTERNS = [
|
|
37
|
+
/(\d{8,})/g, // session codes (8+ digits)
|
|
38
|
+
/(bot\d+:[A-Za-z0-9_-]{30,})/gi, // telegram bot tokens
|
|
39
|
+
/(enc:[A-Za-z0-9+/=.]+)/g, // encrypted values
|
|
40
|
+
/(sk-ant-[A-Za-z0-9_-]+)/g, // anthropic API keys
|
|
41
|
+
/(sk-[A-Za-z0-9]{20,})/g, // openai API keys
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const sanitize = (str: string): string => {
|
|
45
|
+
let result = str;
|
|
46
|
+
for (const pattern of SENSITIVE_PATTERNS) {
|
|
47
|
+
result = result.replace(pattern, (match) => match.slice(0, 4) + '****');
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const format = (...args: any[]): string => {
|
|
53
|
+
return args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ');
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
console.log = (...args: any[]) => {
|
|
57
|
+
const line = `[${ts()}] ${format(...args)}`;
|
|
58
|
+
origLog(line);
|
|
59
|
+
writeToFile(sanitize(line));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
console.error = (...args: any[]) => {
|
|
63
|
+
const line = `[${ts()}] [ERROR] ${format(...args)}`;
|
|
64
|
+
origError(line);
|
|
65
|
+
writeToFile(sanitize(line));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
console.warn = (...args: any[]) => {
|
|
69
|
+
const line = `[${ts()}] [WARN] ${format(...args)}`;
|
|
70
|
+
origWarn(line);
|
|
71
|
+
writeToFile(sanitize(line));
|
|
72
|
+
};
|
|
73
|
+
}
|
package/lib/password.ts
CHANGED
|
@@ -59,7 +59,7 @@ export function getSessionCode(): string {
|
|
|
59
59
|
export function rotateSessionCode(): string {
|
|
60
60
|
const code = generateSessionCode();
|
|
61
61
|
saveSessionCode(code);
|
|
62
|
-
console.log(`[password] New session code:
|
|
62
|
+
console.log(`[password] New session code: ****${code.slice(-2)}`);
|
|
63
63
|
return code;
|
|
64
64
|
}
|
|
65
65
|
|
package/lib/skills.ts
CHANGED
|
@@ -87,6 +87,7 @@ function getInstalledVersion(name: string, type: string, basePath?: string): str
|
|
|
87
87
|
// ─── Sync from registry ──────────────────────────────────────
|
|
88
88
|
|
|
89
89
|
export async function syncSkills(): Promise<{ synced: number; error?: string }> {
|
|
90
|
+
console.log('[skills] Syncing from registry...');
|
|
90
91
|
const baseUrl = getBaseUrl();
|
|
91
92
|
|
|
92
93
|
try {
|
|
@@ -198,6 +199,7 @@ export async function syncSkills(): Promise<{ synced: number; error?: string }>
|
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
201
|
|
|
202
|
+
console.log(`[skills] Synced ${items.length} items`);
|
|
201
203
|
return { synced: items.length };
|
|
202
204
|
} catch (e) {
|
|
203
205
|
return { synced: 0, error: e instanceof Error ? e.message : String(e) };
|
|
@@ -284,6 +286,7 @@ async function downloadToDir(files: { path: string; download_url: string }[], de
|
|
|
284
286
|
// ─── Install ─────────────────────────────────────────────────
|
|
285
287
|
|
|
286
288
|
export async function installGlobal(name: string): Promise<void> {
|
|
289
|
+
console.log(`[skills] Installing "${name}" globally`);
|
|
287
290
|
const row = db().prepare('SELECT type, version FROM skills WHERE name = ?').get(name) as any;
|
|
288
291
|
if (!row) throw new Error(`Not found: ${name}`);
|
|
289
292
|
const type: ItemType = row.type || 'skill';
|
|
@@ -318,6 +321,7 @@ export async function installGlobal(name: string): Promise<void> {
|
|
|
318
321
|
}
|
|
319
322
|
|
|
320
323
|
export async function installProject(name: string, projectPath: string): Promise<void> {
|
|
324
|
+
console.log(`[skills] Installing "${name}" to ${projectPath.split('/').pop()}`);
|
|
321
325
|
const row = db().prepare('SELECT type, version FROM skills WHERE name = ?').get(name) as any;
|
|
322
326
|
if (!row) throw new Error(`Not found: ${name}`);
|
|
323
327
|
const type: ItemType = row.type || 'skill';
|
|
@@ -357,6 +361,7 @@ export async function installProject(name: string, projectPath: string): Promise
|
|
|
357
361
|
// ─── Uninstall ───────────────────────────────────────────────
|
|
358
362
|
|
|
359
363
|
export function uninstallGlobal(name: string): void {
|
|
364
|
+
console.log(`[skills] Uninstalling "${name}" from global`);
|
|
360
365
|
// Remove from all possible locations
|
|
361
366
|
try { rmSync(join(GLOBAL_SKILLS_DIR, name), { recursive: true }); } catch {}
|
|
362
367
|
try { unlinkSync(join(GLOBAL_COMMANDS_DIR, `${name}.md`)); } catch {}
|
|
@@ -367,6 +372,7 @@ export function uninstallGlobal(name: string): void {
|
|
|
367
372
|
}
|
|
368
373
|
|
|
369
374
|
export function uninstallProject(name: string, projectPath: string): void {
|
|
375
|
+
console.log(`[skills] Uninstalling "${name}" from ${projectPath.split('/').pop()}`);
|
|
370
376
|
// Remove from all possible locations
|
|
371
377
|
try { rmSync(join(projectPath, '.claude', 'skills', name), { recursive: true }); } catch {}
|
|
372
378
|
try { unlinkSync(join(projectPath, '.claude', 'commands', `${name}.md`)); } catch {}
|
package/lib/task-manager.ts
CHANGED
|
@@ -405,12 +405,14 @@ function executeTask(task: Task): Promise<void> {
|
|
|
405
405
|
WHERE id = ?
|
|
406
406
|
`).run(resultText, totalCost, task.id);
|
|
407
407
|
emit(task.id, 'status', 'done');
|
|
408
|
+
console.log(`[task] Done: ${task.id} ${task.projectName} (cost: $${totalCost?.toFixed(4) || '0'})`);
|
|
408
409
|
const doneTask = getTask(task.id);
|
|
409
410
|
if (doneTask) notifyTaskComplete(doneTask).catch(() => {});
|
|
410
411
|
notifyTerminalSession(task, 'done', sessionId);
|
|
411
412
|
resolve();
|
|
412
413
|
} else {
|
|
413
414
|
const errMsg = `Process exited with code ${code}`;
|
|
415
|
+
console.error(`[task] Failed: ${task.id} ${task.projectName} — ${errMsg}`);
|
|
414
416
|
updateTaskStatus(task.id, 'failed', errMsg);
|
|
415
417
|
const failedTask = getTask(task.id);
|
|
416
418
|
if (failedTask) notifyTaskFailed(failedTask).catch(() => {});
|
package/next-env.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/types/routes.d.ts";
|
|
3
|
+
import "./.next/dev/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|