@forloop-cc/forloop-opencode-plugin-planner 2.2.1
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/CHANGELOG.md +225 -0
- package/CODE_OF_CONDUCT.md +77 -0
- package/CONTRIBUTING.md +176 -0
- package/LICENSE +21 -0
- package/README.md +211 -0
- package/SECURITY.md +69 -0
- package/docs/CONFIGURATION.md +283 -0
- package/docs/DEVELOPER-VS-USER.md +129 -0
- package/docs/IMPROVED-AUTHENTICATION.md +317 -0
- package/docs/INSTALLATION-SCRIPTS.md +304 -0
- package/docs/PLANNER-AGENT-IMPROVEMENTS.md +88 -0
- package/docs/PLUGIN-ARCHITECTURE.md +308 -0
- package/docs/QUICKSTART-NON-DEV.md +94 -0
- package/docs/README-FOR-HUMANS-AND-AI.md +109 -0
- package/docs/README.md +70 -0
- package/docs/SKILLS-GAP-ANALYSIS.md +157 -0
- package/docs/USER-INTERACTION-MODEL.md +106 -0
- package/docs/file-management/2026-04-12-forloop-aivy-documents-sync.md +395 -0
- package/docs/file-management/FILE_UPLOAD_ENHANCEMENT.md +431 -0
- package/docs/file-management/FILE_UPLOAD_FLOW.md +300 -0
- package/docs/images/forloop-opencode-flow.png +0 -0
- package/docs/images/forloop-opencode-header.png +0 -0
- package/docs/superpowers/plans/2026-05-03-sprint-isolated-directory-structure.md +1450 -0
- package/docs/superpowers/plans/2026-05-29-install-system.md +850 -0
- package/docs/superpowers/specs/2026-05-03-sprint-isolated-directory-structure-design.md +149 -0
- package/docs/superpowers/specs/2026-05-19-opencode-message-recording-design.md +468 -0
- package/docs/superpowers/specs/2026-05-29-install-system-design.md +135 -0
- package/install.sh +317 -0
- package/package.json +87 -0
- package/plugins/capabilities/api-client.ts +556 -0
- package/plugins/capabilities/auth.ts +120 -0
- package/plugins/capabilities/config.ts +195 -0
- package/plugins/capabilities/context-resolver.ts +106 -0
- package/plugins/capabilities/token-storage.ts +98 -0
- package/plugins/forloop-plugin.ts +186 -0
- package/plugins/hooks/message-recorder.ts +258 -0
- package/plugins/hooks/shell-env.ts +62 -0
- package/plugins/tools/agent-sprint-tools.ts +100 -0
- package/plugins/tools/agent-suggestion-tools.ts +275 -0
- package/plugins/tools/agent-tools.ts +114 -0
- package/plugins/tools/ai-trigger-tools.ts +79 -0
- package/plugins/tools/developer-status-tool.ts +97 -0
- package/plugins/tools/file-tools.ts +240 -0
- package/plugins/tools/folder-tools.ts +75 -0
- package/plugins/tools/organization-tools.ts +213 -0
- package/plugins/tools/schedule-tools.ts +173 -0
- package/plugins/tools/sprint-tools.ts +287 -0
- package/plugins/tools/story-tools.ts +320 -0
- package/plugins/tools/sync-tools.ts +509 -0
- package/plugins/tools/template-tools.ts +170 -0
- package/plugins/tools/user-tools.ts +146 -0
- package/scripts/migrate-tokens.js +90 -0
- package/scripts/use-prod.sh +11 -0
- package/tsconfig.json +17 -0
- package/update.sh +172 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Skills Gap Analysis & Improvements
|
|
2
|
+
|
|
3
|
+
## Current State Analysis
|
|
4
|
+
|
|
5
|
+
### ✅ What Skills Document Well
|
|
6
|
+
|
|
7
|
+
| Skill | Documented Workflow | Status |
|
|
8
|
+
|-------|-------------------|--------|
|
|
9
|
+
| `forloop-context` | Session startup, S3 sync (Step 7) | ✅ Documented |
|
|
10
|
+
| `task-tracking` | Create stories → Write task.md → Upload S3 with doc_folder | ✅ Documented |
|
|
11
|
+
| `plan-documentation` | Create plan → Upload S3 with doc_folder | ✅ Documented |
|
|
12
|
+
| `knowledge-management` | Capture knowledge → Upload S3 with doc_folder | ✅ Documented |
|
|
13
|
+
| `aivy-documents-sync` | Session start sync, post-change sync | ✅ Documented |
|
|
14
|
+
|
|
15
|
+
### ❌ What Agent Actually Does (Problems)
|
|
16
|
+
|
|
17
|
+
**The agent definition (`forLoopPlanner.md`) is the PROBLEM.** It has its own simplified workflow that SKIPS critical steps documented in the skills:
|
|
18
|
+
|
|
19
|
+
| Expected (from Skills) | Agent Does (from Definition) | Gap |
|
|
20
|
+
|------------------------|------------------------------|-----|
|
|
21
|
+
| Sync S3 → local on session start | Only loads local `.forloop/` | ❌ Missing |
|
|
22
|
+
| Create/verify doc_folder before uploads | No mention of doc_folder | ❌ Missing |
|
|
23
|
+
| Upload task.md to S3 with `--storyId` | "Upload task file to S3" (no doc_folder) | ❌ Incomplete |
|
|
24
|
+
| Upload plan.md to S3 with `--storyId` | "Upload to S3" (no doc_folder) | ❌ Incomplete |
|
|
25
|
+
| Upload knowledge to S3 with `--storyId` | No knowledge upload mentioned | ❌ Missing |
|
|
26
|
+
| Verify uploads with `forloopFileList` | No verification step | ❌ Missing |
|
|
27
|
+
|
|
28
|
+
## Root Cause
|
|
29
|
+
|
|
30
|
+
**The agent definition is the source of truth for the LLM**, not the skills. The skills provide detailed workflows, but the agent definition gives the high-level instructions. If the agent definition doesn't include a step, the LLM will skip it.
|
|
31
|
+
|
|
32
|
+
## Required Improvements
|
|
33
|
+
|
|
34
|
+
### 1. Agent Definition (`agent/forLoopPlanner.md`)
|
|
35
|
+
|
|
36
|
+
**Add to Session Startup Flow:**
|
|
37
|
+
```markdown
|
|
38
|
+
### Step 0.5: Sync from S3 (MANDATORY)
|
|
39
|
+
|
|
40
|
+
After loading local context, ALWAYS sync from S3:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
forloopSyncAivyFolder --sprintId {sprintId}
|
|
44
|
+
forloopSyncS3ToLocal --sprintId {sprintId}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This ensures you have the latest plan, task, and knowledge files from the team.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Add to Workflow:**
|
|
51
|
+
```markdown
|
|
52
|
+
### Doc Folder Management (ALWAYS BEFORE UPLOADS)
|
|
53
|
+
|
|
54
|
+
Before uploading ANY file to S3:
|
|
55
|
+
|
|
56
|
+
1. Ensure doc_folder exists:
|
|
57
|
+
```bash
|
|
58
|
+
forloopSyncAivyFolder --sprintId {sprintId}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
2. Get doc_folder story ID:
|
|
62
|
+
```bash
|
|
63
|
+
forloopAivyDocGet --sprintId {sprintId}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
3. Upload with linking:
|
|
67
|
+
```bash
|
|
68
|
+
forloopSyncLocalToS3 \
|
|
69
|
+
--filePath {path} \
|
|
70
|
+
--sprintId {sprintId} \
|
|
71
|
+
--folder project/{plans,tasks,knowledge} \
|
|
72
|
+
--storyId {docFolderId}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
4. Verify upload:
|
|
76
|
+
```bash
|
|
77
|
+
forloopFileList --sprintId {sprintId}
|
|
78
|
+
```
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2. Sync Tools (`plugins/tools/sync-tools.ts`)
|
|
82
|
+
|
|
83
|
+
**Verify `forloopSyncAivyFolder` tool:**
|
|
84
|
+
- Checks if "Aivy Plan Doc" doc_folder exists
|
|
85
|
+
- Creates it if not
|
|
86
|
+
- Returns the doc_folder story ID
|
|
87
|
+
|
|
88
|
+
**Verify `forloopSyncS3ToLocal` tool:**
|
|
89
|
+
- Downloads all files from S3 `project/plans/`, `project/tasks/`, `project/knowledge/`
|
|
90
|
+
- Saves to `~/.forloop/plan/`, `~/.forloop/task/`, `~/.forloop/knowledge/`
|
|
91
|
+
- Updates manifest.json
|
|
92
|
+
|
|
93
|
+
**Verify `forloopSyncLocalToS3` tool:**
|
|
94
|
+
- Uploads file to correct S3 folder
|
|
95
|
+
- Links to doc_folder via `storyId` parameter
|
|
96
|
+
- Returns upload confirmation
|
|
97
|
+
|
|
98
|
+
### 3. Skill Improvements
|
|
99
|
+
|
|
100
|
+
**forloop-context/SKILL.md:**
|
|
101
|
+
- Make Step 7 (S3 sync) more prominent
|
|
102
|
+
- Add explicit commands with parameters
|
|
103
|
+
- Add error handling for sync failures
|
|
104
|
+
|
|
105
|
+
**task-tracking/SKILL.md:**
|
|
106
|
+
- Already well-documented, but add verification checklist
|
|
107
|
+
- Emphasize doc_folder linking as MANDATORY
|
|
108
|
+
|
|
109
|
+
**plan-documentation/SKILL.md:**
|
|
110
|
+
- Already well-documented, but add verification checklist
|
|
111
|
+
- Emphasize doc_folder linking as MANDATORY
|
|
112
|
+
|
|
113
|
+
## Implementation Priority
|
|
114
|
+
|
|
115
|
+
1. **HIGH**: Update agent definition (forLoopPlanner.md) - This is the LLM's primary instructions
|
|
116
|
+
2. **HIGH**: Verify sync tools implement doc_folder workflow correctly
|
|
117
|
+
3. **MEDIUM**: Update skills to be more explicit about mandatory steps
|
|
118
|
+
4. **LOW**: Add verification checklists to all skills
|
|
119
|
+
|
|
120
|
+
## Expected Behavior After Fix
|
|
121
|
+
|
|
122
|
+
### Session Start
|
|
123
|
+
```
|
|
124
|
+
1. Load local .forloop/ context
|
|
125
|
+
2. Sync from S3 → local (get latest files)
|
|
126
|
+
3. Present combined context summary
|
|
127
|
+
4. Confirm sprint with user
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Creating Plan
|
|
131
|
+
```
|
|
132
|
+
1. Write plan to ~/.forloop/plan/
|
|
133
|
+
2. Update manifest.json
|
|
134
|
+
3. Ensure doc_folder exists (create if needed)
|
|
135
|
+
4. Get doc_folder ID
|
|
136
|
+
5. Upload plan to S3 with --storyId {docFolderId}
|
|
137
|
+
6. Verify upload with forloopFileList
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Creating Stories
|
|
141
|
+
```
|
|
142
|
+
1. Write task.md to ~/.forloop/task/
|
|
143
|
+
2. Update manifest.json
|
|
144
|
+
3. Ensure doc_folder exists (should already exist from plan)
|
|
145
|
+
4. Get doc_folder ID
|
|
146
|
+
5. Upload task.md to S3 with --storyId {docFolderId}
|
|
147
|
+
6. Verify upload with forloopFileList
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Capturing Knowledge
|
|
151
|
+
```
|
|
152
|
+
1. Write knowledge file to ~/.forloop/knowledge/
|
|
153
|
+
2. Ensure doc_folder exists
|
|
154
|
+
3. Get doc_folder ID
|
|
155
|
+
4. Upload to S3 with --storyId {docFolderId}
|
|
156
|
+
5. Verify upload with forloopFileList
|
|
157
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# How Users Interact with ForLoop Plugin
|
|
2
|
+
|
|
3
|
+
## Understanding the Architecture
|
|
4
|
+
|
|
5
|
+
The ForLoop Plugin is **NOT a CLI tool** that you run directly. It's an **opencode plugin** that extends opencode's capabilities.
|
|
6
|
+
|
|
7
|
+
### ❌ What Users DON'T Do
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# These DON'T work:
|
|
11
|
+
node plugins/tools/sprint-tools.ts ✗
|
|
12
|
+
npm run forloopSprintList ✗
|
|
13
|
+
./plugins/forloop-plugin.ts ✗
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### ✅ What Users DO Instead
|
|
17
|
+
|
|
18
|
+
Users interact through **opencode chat** using tool commands:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1. Start opencode
|
|
22
|
+
opencode
|
|
23
|
+
|
|
24
|
+
# 2. In the chat, use tool commands:
|
|
25
|
+
forloopSprintList
|
|
26
|
+
forloopStoryCreate --title "As a user, I want to login" --sprintId 14
|
|
27
|
+
forloopAgentBreakdown --storyId 78
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Execution Flow
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
|
|
34
|
+
│ User │ │ opencode │ │ ForLoop │
|
|
35
|
+
│ │ │ │ │ Plugin │
|
|
36
|
+
└──────┬──────┘ └──────┬───────┘ └──────┬──────┘
|
|
37
|
+
│ │ │
|
|
38
|
+
│ 1. Type command │ │
|
|
39
|
+
│───────────────────>│ │
|
|
40
|
+
│ │ │
|
|
41
|
+
│ │ 2. Route to plugin │
|
|
42
|
+
│ │─────────────────────>│
|
|
43
|
+
│ │ │
|
|
44
|
+
│ │ │ 3. Execute
|
|
45
|
+
│ │ │ (TypeScript code)
|
|
46
|
+
│ │ │
|
|
47
|
+
│ │ 4. Return result │
|
|
48
|
+
│ │<─────────────────────│
|
|
49
|
+
│ │ │
|
|
50
|
+
│ 5. Show output │ │
|
|
51
|
+
│<───────────────────│ │
|
|
52
|
+
│ │ │
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Tool Commands vs TypeScript Functions
|
|
56
|
+
|
|
57
|
+
| TypeScript Function | User Command |
|
|
58
|
+
|---------------------|--------------|
|
|
59
|
+
| `createSprintListTool()` | `forloopSprintList` |
|
|
60
|
+
| `createStoryCreateTool()` | `forloopStoryCreate --title "..."` |
|
|
61
|
+
| `createAgentBreakdownTool()` | `forloopAgentBreakdown --storyId 78` |
|
|
62
|
+
|
|
63
|
+
## For Developers
|
|
64
|
+
|
|
65
|
+
If you're **developing** the plugin:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# 1. Make changes to TypeScript files
|
|
69
|
+
edit plugins/tools/sprint-tools.ts
|
|
70
|
+
|
|
71
|
+
# 2. Run tests
|
|
72
|
+
npm run test:unit
|
|
73
|
+
|
|
74
|
+
# 3. Install locally to test
|
|
75
|
+
npm run install:local
|
|
76
|
+
|
|
77
|
+
# 4. Test in opencode
|
|
78
|
+
opencode
|
|
79
|
+
> forloopSprintList
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
If you're **using** the plugin:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Just use opencode chat
|
|
86
|
+
opencode
|
|
87
|
+
> forloopSprintList
|
|
88
|
+
|
|
89
|
+
# That's it! No TypeScript knowledge needed.
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Summary
|
|
93
|
+
|
|
94
|
+
| Aspect | Developer | End User |
|
|
95
|
+
|--------|-----------|----------|
|
|
96
|
+
| Touches TypeScript? | ✅ Yes | ❌ No |
|
|
97
|
+
| Runs `npm` commands? | ✅ Yes | ❌ No |
|
|
98
|
+
| Uses `forloop.*` commands? | ✅ Yes | ✅ Yes |
|
|
99
|
+
| Needs to understand plugin architecture? | ✅ Yes | ❌ No |
|
|
100
|
+
|
|
101
|
+
**End users only need to know:**
|
|
102
|
+
1. Install the plugin
|
|
103
|
+
2. Set up API token
|
|
104
|
+
3. Use `forloop.*` commands in opencode chat
|
|
105
|
+
|
|
106
|
+
That's it!
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# ForLoop Aivy Documents - Auto-Sync Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Implement automatic bidirectional file synchronization between local `~/.forloop/` folder and S3, with auto-creation of "ForLoop Aivy Documents" doc_folder for organized file management.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Enhance existing skills with sync capabilities - extends forloop-context skill for S3→local sync at session start, and enhances knowledge-management/plan-documentation/task-tracking skills for local→S3 sync on file changes.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, ForLoop plugin tools, AWS S3, Express.js backend
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Requirements Summary
|
|
14
|
+
|
|
15
|
+
Based on user requirements:
|
|
16
|
+
|
|
17
|
+
1. **Doc Folder Auto-Creation**: Agent checks if doc_folder "ForLoop Aivy Documents" exists in working sprint, creates if not
|
|
18
|
+
2. **S3 → Local Sync**: At session start (after manifest.json confirms active sprint), sync files from S3 to local `~/.forloop/`
|
|
19
|
+
3. **Local → S3 Sync**: Immediate sync whenever files are created/updated/removed under `~/.forloop/*`
|
|
20
|
+
4. **S3 Structure**: Keep current structure: `sprint/{id}/project/{plans,knowledge,tasks}/`
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## File Structure
|
|
25
|
+
|
|
26
|
+
### New Files to Create
|
|
27
|
+
|
|
28
|
+
1. **Skill:** `forloop-opencode-plugin-planner/skills/aivy-documents-sync/SKILL.md`
|
|
29
|
+
- Auto-sync skill for bidirectional synchronization
|
|
30
|
+
- Integrates with forloop-context for session startup
|
|
31
|
+
- Handles doc_folder creation and file tracking
|
|
32
|
+
|
|
33
|
+
2. **Plugin Tool:** `forloop-opencode-plugin-planner/plugins/tools/sync-tools.ts`
|
|
34
|
+
- `createSyncTool()` - Main sync orchestration
|
|
35
|
+
- `createDocFolderCheckTool()` - Auto-create doc_folder
|
|
36
|
+
- Helper functions for file comparison
|
|
37
|
+
|
|
38
|
+
3. **Test File:** `forloop-opencode-plugin-planner/test/suites/integration/sync-api.test.ts`
|
|
39
|
+
- Integration tests for sync functionality
|
|
40
|
+
|
|
41
|
+
### Modified Files
|
|
42
|
+
|
|
43
|
+
1. **forloop-context/SKILL.md** - Add sync step after loading manifest
|
|
44
|
+
2. **knowledge-management/SKILL.md** - Add sync trigger after file creation
|
|
45
|
+
3. **plan-documentation/SKILL.md** - Add sync trigger after file creation
|
|
46
|
+
4. **task-tracking/SKILL.md** - Add sync trigger after file creation
|
|
47
|
+
5. **file-tools.ts** - Add sync tracking capabilities
|
|
48
|
+
6. **api-client.ts** - Add file comparison/download methods
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Implementation Tasks
|
|
53
|
+
|
|
54
|
+
### Task 1: Create Sync Tools Plugin
|
|
55
|
+
|
|
56
|
+
**Files:**
|
|
57
|
+
- Create: `forloop-opencode-plugin-planner/plugins/tools/sync-tools.ts`
|
|
58
|
+
- Modify: `forloop-opencode-plugin-planner/plugins/forloop-plugin.ts:106-108`
|
|
59
|
+
- Test: `forloop-opencode-plugin-planner/test/suites/unit/sync-tools.test.ts`
|
|
60
|
+
|
|
61
|
+
- [ ] **Step 1: Create sync-tools.ts with doc_folder check tool**
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { tool } from '@opencode-ai/plugin';
|
|
65
|
+
import { ForLoopAPIClient } from '../capabilities/api-client';
|
|
66
|
+
import { validateToken } from '../capabilities/auth';
|
|
67
|
+
import { resolveSprintId } from '../capabilities/context-resolver';
|
|
68
|
+
|
|
69
|
+
export function createDocFolderCheckTool(client: ForLoopAPIClient) {
|
|
70
|
+
return tool({
|
|
71
|
+
description: 'Check and auto-create "ForLoop Aivy Documents" doc_folder in sprint',
|
|
72
|
+
args: {
|
|
73
|
+
sprintId: tool.schema.number()
|
|
74
|
+
.optional()
|
|
75
|
+
.describe('Target sprint ID (auto-detected from context)'),
|
|
76
|
+
},
|
|
77
|
+
async execute(args, context) {
|
|
78
|
+
const tokenResult = await validateToken();
|
|
79
|
+
if (!tokenResult.valid) {
|
|
80
|
+
return tokenResult.error;
|
|
81
|
+
}
|
|
82
|
+
client.setToken?.(tokenResult.token);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// Resolve sprint ID
|
|
86
|
+
const resolution = await resolveSprintId(args.sprintId, context.directory);
|
|
87
|
+
if (!resolution.sprintId) {
|
|
88
|
+
return '❌ No sprint ID available. Please select a sprint first.';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Get sprint details including stories
|
|
92
|
+
const sprint = await client.getSprint(resolution.sprintId);
|
|
93
|
+
|
|
94
|
+
// Check if "ForLoop Aivy Documents" doc_folder exists
|
|
95
|
+
const docFolderStory = sprint.stories?.find((story: any) =>
|
|
96
|
+
story.type === 'doc_folder' &&
|
|
97
|
+
story.title === 'ForLoop Aivy Documents'
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (docFolderStory) {
|
|
101
|
+
return `✅ "ForLoop Aivy Documents" folder already exists (Story #${docFolderStory.id})`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Create doc_folder if not exists
|
|
105
|
+
const newFolder = await client.createStory({
|
|
106
|
+
title: 'ForLoop Aivy Documents',
|
|
107
|
+
description: ' Auto-created folder for ForLoop Aivy planning documents and sync',
|
|
108
|
+
sprintId: resolution.sprintId,
|
|
109
|
+
type: 'doc_folder',
|
|
110
|
+
metadata: JSON.stringify({
|
|
111
|
+
permissions: 'team',
|
|
112
|
+
autoCreated: true,
|
|
113
|
+
purpose: 'aivy-sync',
|
|
114
|
+
}),
|
|
115
|
+
status: 'todo',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return `✅ Created "ForLoop Aivy Documents" folder (Story #${newFolder.id})`;
|
|
119
|
+
} catch (error: any) {
|
|
120
|
+
return `❌ Error: ${error.message}`;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- [ ] **Step 2: Add createS3ToLocalSyncTool function**
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
export function createS3ToLocalSyncTool(client: ForLoopAPIClient) {
|
|
131
|
+
return tool({
|
|
132
|
+
description: 'Sync files from S3 to local ~/.forloop/ folder',
|
|
133
|
+
args: {
|
|
134
|
+
sprintId: tool.schema.number()
|
|
135
|
+
.optional()
|
|
136
|
+
.describe('Target sprint ID'),
|
|
137
|
+
syncKnowledge: tool.schema.boolean()
|
|
138
|
+
.default(true)
|
|
139
|
+
.describe('Sync knowledge files'),
|
|
140
|
+
syncPlans: tool.schema.boolean()
|
|
141
|
+
.default(true)
|
|
142
|
+
.describe('Sync plan files'),
|
|
143
|
+
syncTasks: tool.schema.boolean()
|
|
144
|
+
.default(true)
|
|
145
|
+
.describe('Sync task files'),
|
|
146
|
+
},
|
|
147
|
+
async execute(args, context) {
|
|
148
|
+
const tokenResult = await validateToken();
|
|
149
|
+
if (!tokenResult.valid) {
|
|
150
|
+
return tokenResult.error;
|
|
151
|
+
}
|
|
152
|
+
client.setToken?.(tokenResult.token);
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const resolution = await resolveSprintId(args.sprintId, context.directory);
|
|
156
|
+
if (!resolution.sprintId) {
|
|
157
|
+
return '❌ No sprint ID available.';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get files from S3
|
|
161
|
+
const s3Files = await client.getSprintFiles(resolution.sprintId);
|
|
162
|
+
|
|
163
|
+
// Filter files by folder pattern
|
|
164
|
+
const knowledgeFiles = s3Files.filter((f: any) =>
|
|
165
|
+
f.url?.includes('/project/knowledge/')
|
|
166
|
+
);
|
|
167
|
+
const planFiles = s3Files.filter((f: any) =>
|
|
168
|
+
f.url?.includes('/project/plans/')
|
|
169
|
+
);
|
|
170
|
+
const taskFiles = s3Files.filter((f: any) =>
|
|
171
|
+
f.url?.includes('/project/tasks/')
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Download and save locally
|
|
175
|
+
const homeDir = process.env.HOME || '/tmp/home';
|
|
176
|
+
let syncedCount = 0;
|
|
177
|
+
|
|
178
|
+
if (args.syncKnowledge) {
|
|
179
|
+
for (const file of knowledgeFiles) {
|
|
180
|
+
const downloadUrl = await client.getFileOpenUrl(file.id);
|
|
181
|
+
const fileName = file.originalName || file.filename;
|
|
182
|
+
// Save to ~/.forloop/knowledge/
|
|
183
|
+
// Increment syncedCount
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Repeat for plans and tasks...
|
|
188
|
+
|
|
189
|
+
return `✅ Synced ${syncedCount} files from S3 to ~/.forloop/`;
|
|
190
|
+
} catch (error: any) {
|
|
191
|
+
return `❌ Error: ${error.message}`;
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
- [ ] **Step 3: Add createLocalToS3SyncTool function**
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
export function createLocalToS3SyncTool(client: ForLoopAPIClient) {
|
|
202
|
+
return tool({
|
|
203
|
+
description: 'Sync local ~/.forloop/ files to S3',
|
|
204
|
+
args: {
|
|
205
|
+
filePath: tool.schema.string()
|
|
206
|
+
.describe('Local file path to sync'),
|
|
207
|
+
sprintId: tool.schema.number()
|
|
208
|
+
.optional()
|
|
209
|
+
.describe('Target sprint ID'),
|
|
210
|
+
},
|
|
211
|
+
async execute(args, context) {
|
|
212
|
+
// Implementation for immediate sync of individual files
|
|
213
|
+
// Used by knowledge-management, plan-documentation, task-tracking skills
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
- [ ] **Step 4: Register tools in forloop-plugin.ts**
|
|
220
|
+
|
|
221
|
+
**File:** `forloop-opencode-plugin-planner/plugins/forloop-plugin.ts`
|
|
222
|
+
|
|
223
|
+
**Locate lines 106-108:**
|
|
224
|
+
```typescript
|
|
225
|
+
'forloopFileUpload': createFileUploadTool(client),
|
|
226
|
+
'forloopFileList': createFileListTool(client),
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Add after file tools:**
|
|
230
|
+
```typescript
|
|
231
|
+
'forloop.sync.folder': createDocFolderCheckTool(client),
|
|
232
|
+
'forloopSyncS3ToLocal': createS3ToLocalSyncTool(client),
|
|
233
|
+
'forloopSyncLocalToS3': createLocalToS3SyncTool(client),
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
- [ ] **Step 5: Write unit tests**
|
|
237
|
+
|
|
238
|
+
**File:** `forloop-opencode-plugin-planner/test/suites/unit/sync-tools.test.ts`
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
242
|
+
import {
|
|
243
|
+
createDocFolderCheckTool,
|
|
244
|
+
createS3ToLocalSyncTool,
|
|
245
|
+
createLocalToS3SyncTool,
|
|
246
|
+
} from '../../../plugins/tools/sync-tools';
|
|
247
|
+
|
|
248
|
+
describe('Sync Tools', () => {
|
|
249
|
+
const mockClient = {
|
|
250
|
+
getSprint: vi.fn(),
|
|
251
|
+
createStory: vi.fn(),
|
|
252
|
+
getSprintFiles: vi.fn(),
|
|
253
|
+
getFileOpenUrl: vi.fn(),
|
|
254
|
+
// ... other mocks
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
describe('forloop.sync.folder', () => {
|
|
258
|
+
it('should detect existing doc_folder', async () => {
|
|
259
|
+
mockClient.getSprint.mockResolvedValue({
|
|
260
|
+
id: 14,
|
|
261
|
+
stories: [
|
|
262
|
+
{ id: 101, type: 'doc_folder', title: 'ForLoop Aivy Documents' }
|
|
263
|
+
]
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const tool = createDocFolderCheckTool(mockClient as any);
|
|
267
|
+
const result = await tool.executor({ sprintId: 14 }, { directory: '.' });
|
|
268
|
+
|
|
269
|
+
expect(result).toContain('already exists');
|
|
270
|
+
expect(mockClient.createStory).not.toHaveBeenCalled();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('should create doc_folder if not exists', async () => {
|
|
274
|
+
mockClient.getSprint.mockResolvedValue({
|
|
275
|
+
id: 14,
|
|
276
|
+
stories: []
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
mockClient.createStory.mockResolvedValue({
|
|
280
|
+
id: 102,
|
|
281
|
+
title: 'ForLoop Aivy Documents',
|
|
282
|
+
type: 'doc_folder'
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const tool = createDocFolderCheckTool(mockClient as any);
|
|
286
|
+
const result = await tool.executor({ sprintId: 14 }, { directory: '.' });
|
|
287
|
+
|
|
288
|
+
expect(result).toContain('Created');
|
|
289
|
+
expect(mockClient.createStory).toHaveBeenCalledWith(expect.objectContaining({
|
|
290
|
+
title: 'ForLoop Aivy Documents',
|
|
291
|
+
type: 'doc_folder'
|
|
292
|
+
}));
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
- [ ] **Step 6: Run tests**
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
cd forloop-opencode-plugin-planner
|
|
302
|
+
npm run test:unit -- sync-tools
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Expected: All tests PASS
|
|
306
|
+
|
|
307
|
+
- [ ] **Step 7: Commit**
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
git add plugins/tools/sync-tools.ts plugins/forloop-plugin.ts test/suites/unit/sync-tools.test.ts
|
|
311
|
+
git commit -m "feat: add ForLoop Aivy documents sync tools"
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
### Task 2: Create Auto-Sync Skill
|
|
317
|
+
|
|
318
|
+
**Files:**
|
|
319
|
+
- Create: `forloop-opencode-plugin-planner/skills/aivy-documents-sync/SKILL.md`
|
|
320
|
+
- Modify: `forloop-opencode-plugin-planner/skills/forloop-context/SKILL.md:28-54`
|
|
321
|
+
|
|
322
|
+
- [ ] **Step 1: Create aivy-documents-sync skill**
|
|
323
|
+
|
|
324
|
+
Create comprehensive skill document (see full content in planning document) covering:
|
|
325
|
+
- Session startup sync workflow
|
|
326
|
+
- File change sync triggers
|
|
327
|
+
- Integration with existing skills
|
|
328
|
+
- Sync manifest tracking
|
|
329
|
+
- Error handling
|
|
330
|
+
- Conflict resolution
|
|
331
|
+
|
|
332
|
+
- [ ] **Step 2: Update forloop-context skill**
|
|
333
|
+
|
|
334
|
+
**File:** `forloop-opencode-plugin-planner/skills/forloop-context/SKILL.md`
|
|
335
|
+
|
|
336
|
+
Add Step 7 after "Present to user":
|
|
337
|
+
```markdown
|
|
338
|
+
### Step 7: Auto-Sync Files (NEW)
|
|
339
|
+
1. Check and create doc_folder: `forloop.sync.folder --sprintId {id}`
|
|
340
|
+
2. Sync S3 to local: `forloopSyncS3ToLocal --sprintId {id}`
|
|
341
|
+
3. Report sync status
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
- [ ] **Step 3: Update other skills**
|
|
345
|
+
|
|
346
|
+
Add sync tracking steps to:
|
|
347
|
+
- knowledge-management/SKILL.md (after upload)
|
|
348
|
+
- plan-documentation/SKILL.md (after upload)
|
|
349
|
+
- task-tracking/SKILL.md (after upload)
|
|
350
|
+
|
|
351
|
+
- [ ] **Step 4: Commit**
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
git add skills/*.md
|
|
355
|
+
git commit -m "feat: add ForLoop Aivy auto-sync skill"
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### Task 3: Extend API Client
|
|
361
|
+
|
|
362
|
+
**Files:**
|
|
363
|
+
- Modify: `forloop-opencode-plugin-planner/plugins/capabilities/api-client.ts`
|
|
364
|
+
|
|
365
|
+
- [ ] **Step 1: Add downloadFile method**
|
|
366
|
+
- [ ] **Step 2: Add compareFiles method**
|
|
367
|
+
- [ ] **Step 3: Commit**
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
### Task 4: Write Integration Tests
|
|
372
|
+
|
|
373
|
+
**Files:**
|
|
374
|
+
- Create: `forloop-opencode-plugin-planner/test/suites/integration/sync-api.test.ts`
|
|
375
|
+
|
|
376
|
+
- [ ] **Step 1: Create test file**
|
|
377
|
+
- [ ] **Step 2: Run tests**
|
|
378
|
+
- [ ] **Step 3: Commit**
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Execution Handoff
|
|
383
|
+
|
|
384
|
+
**Plan complete. Two execution options:**
|
|
385
|
+
|
|
386
|
+
**1. Subagent-Driven (recommended)** - Fresh subagent per task, review between tasks, fast iteration
|
|
387
|
+
|
|
388
|
+
**2. Inline Execution** - Execute tasks in this session with checkpoints
|
|
389
|
+
|
|
390
|
+
**Which approach?**
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
**Created:** 2026-04-12
|
|
395
|
+
**Version:** 1.0.0
|