first commit
This commit is contained in:
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
# ── GSD baseline (auto-generated) ──
|
||||||
|
.gsd/activity/
|
||||||
|
.gsd/runtime/
|
||||||
|
.gsd/auto.lock
|
||||||
|
.gsd/metrics.json
|
||||||
|
.gsd/STATE.md
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.code-workspace
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
node_modules/
|
||||||
|
.next/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
target/
|
||||||
|
vendor/
|
||||||
|
*.log
|
||||||
|
coverage/
|
||||||
|
.cache/
|
||||||
|
tmp/
|
||||||
116
.planning/codebase/ARCHITECTURE.md
Normal file
116
.planning/codebase/ARCHITECTURE.md
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# Architecture
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-03-12
|
||||||
|
|
||||||
|
## Pattern Overview
|
||||||
|
|
||||||
|
**Overall:** Agent-based task automation framework built on OpenCode
|
||||||
|
|
||||||
|
**Key Characteristics:**
|
||||||
|
- Modular command/agent system with specialized agents for different phases
|
||||||
|
- Workflow-driven execution with named phases (discovery, research, plan, execute, verify)
|
||||||
|
- Direct file output pattern - agents write to `.planning/codebase/` directly
|
||||||
|
- Subagent spawning for parallel exploration tasks
|
||||||
|
|
||||||
|
## Layers
|
||||||
|
|
||||||
|
**Commands Layer:**
|
||||||
|
- Purpose: User-facing entry points for GSD operations
|
||||||
|
- Location: `.opencode/commands/gsd/`
|
||||||
|
- Contains: Markdown command definitions with YAML frontmatter
|
||||||
|
- Files: 33 command files (gsd-*.md)
|
||||||
|
- Used by: OpenCode CLI when user invokes `/gsd-*` commands
|
||||||
|
|
||||||
|
**Agents Layer:**
|
||||||
|
- Purpose: Specialized AI agents for focused tasks
|
||||||
|
- Location: `.opencode/agents/`
|
||||||
|
- Contains: Agent role definitions with tools and skills
|
||||||
|
- Files: 12 agent definitions (gsd-*.md)
|
||||||
|
- Used by: Commands that need parallel or complex task execution
|
||||||
|
|
||||||
|
**Workflows Layer:**
|
||||||
|
- Purpose: Execution logic connecting commands and agents
|
||||||
|
- Location: `.opencode/get-shit-done/workflows/`
|
||||||
|
- Contains: Step-by-step workflow definitions
|
||||||
|
- Files: 36 workflow files
|
||||||
|
- Depends on: Commands layer (referenced in command execution_context)
|
||||||
|
- Used by: Command execution context
|
||||||
|
|
||||||
|
**Supporting Layers:**
|
||||||
|
- Skills: `.opencode/skills/` - Specialized skills (e.g., model selection)
|
||||||
|
- Rules: `.opencode/rules/` - Behavior rules (e.g., work-hard rule)
|
||||||
|
- Configuration: `.opencode/package.json`, `bun.lock` - Dependencies and lockfile
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
**Command Invocation Flow:**
|
||||||
|
1. User invokes `/gsd-*` command
|
||||||
|
2. Command definition loads execution_context (workflow)
|
||||||
|
3. Workflow orchestrates agent spawning and task execution
|
||||||
|
4. Agents write outputs directly to `.planning/` directories
|
||||||
|
5. Confirmation returned to orchestrator (not full content)
|
||||||
|
|
||||||
|
**Parallel Agent Pattern:**
|
||||||
|
- map-codebase spawns 4 parallel mapper agents (tech, arch, quality, concerns)
|
||||||
|
- Each agent explores focus area and writes specific documents
|
||||||
|
- Agents return confirmations only to minimize context load
|
||||||
|
|
||||||
|
## Key Abstractions
|
||||||
|
|
||||||
|
**Command Pattern:**
|
||||||
|
- Format: Markdown with YAML frontmatter
|
||||||
|
- Structure: name, description, argument-hint, permissions, objective, execution_context, context, when_to_use, process, success_criteria
|
||||||
|
- Location: `.opencode/commands/gsd/gsd-{name}.md`
|
||||||
|
|
||||||
|
**Agent Pattern:**
|
||||||
|
- Format: Markdown with YAML frontmatter
|
||||||
|
- Structure: name, description, mode, tools, color, skills
|
||||||
|
- Location: `.opencode/agents/gsd-{name}.md`
|
||||||
|
- Types: orchestrator agents (main), subagents (parallel workers)
|
||||||
|
|
||||||
|
**Workflow Pattern:**
|
||||||
|
- Format: Markdown with step-by-step instructions
|
||||||
|
- Location: `.opencode/get-shit-done/workflows/{name}.md`
|
||||||
|
- Called from: Command's `<execution_context>` directive
|
||||||
|
|
||||||
|
## Entry Points
|
||||||
|
|
||||||
|
**Primary Entry Point:**
|
||||||
|
- Location: `.opencode/commands/gsd/gsd-*.md` (33 commands)
|
||||||
|
- Triggers: OpenCode CLI `/gsd-*` invocation
|
||||||
|
- Responsibilities: Define command interface, load workflow, return success criteria
|
||||||
|
|
||||||
|
**Command Categories:**
|
||||||
|
- Project management: new-project, new-milestone, resume-work, pause-work
|
||||||
|
- Phase management: add-phase, remove-phase, insert-phase, list-phase-assumptions
|
||||||
|
- Execution: plan-phase, execute-phase, verify-phase, validate-phase
|
||||||
|
- Discovery: research-phase, map-codebase, discuss-phase, quick
|
||||||
|
- Verification: audit-milestone, complete-milestone, verify-work, health
|
||||||
|
- Debugging: debug, diagnose-issues, reapply-patches
|
||||||
|
- Utilities: add-todo, add-tests, set-profile, settings, help
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
**Workflow Error Handling:**
|
||||||
|
- Phase failures trigger diagnosis workflows
|
||||||
|
- verify-work performs health checks on each phase
|
||||||
|
- audit-milestone catches incomplete work before completion
|
||||||
|
|
||||||
|
**Agent Error Patterns:**
|
||||||
|
- Subagent failures reported to orchestrator with error details
|
||||||
|
- Parallel agents - one failure doesn't cascade to others
|
||||||
|
|
||||||
|
## Cross-Cutting Concerns
|
||||||
|
|
||||||
|
**State Management:**
|
||||||
|
- Project state stored in `.planning/STATE.md`
|
||||||
|
- Milestone data in `.gsd/milestones/`
|
||||||
|
- Codebase docs in `.planning/codebase/`
|
||||||
|
|
||||||
|
**Permissions System:**
|
||||||
|
- Commands declare required permissions (read, bash, glob, grep, write, task)
|
||||||
|
- Agents declare tools they can use
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Architecture analysis: 2026-03-12*
|
||||||
186
.planning/codebase/CONCERNS.md
Normal file
186
.planning/codebase/CONCERNS.md
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
# Codebase Concerns
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-03-12
|
||||||
|
|
||||||
|
## Project Status: Greenfield
|
||||||
|
|
||||||
|
**Critical Finding:** This is a greenfield project with no actual implementation code. The repository contains only:
|
||||||
|
|
||||||
|
- `THE_IDEA.md` - Requirements specification document
|
||||||
|
- `.opencode/` - OpenCode/GSD framework configuration
|
||||||
|
|
||||||
|
**Impact:** There are no code-level technical concerns to document because nothing has been built yet. This document outlines concerns for the future implementation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Concerns
|
||||||
|
|
||||||
|
### No Source Code Exists
|
||||||
|
|
||||||
|
**Issue:** No application code has been implemented
|
||||||
|
- Files: None (project has not been initialized with source code)
|
||||||
|
- Impact: Cannot assess code quality, patterns, or technical debt
|
||||||
|
- Fix approach: Initialize project with proper scaffolding before implementation begins
|
||||||
|
|
||||||
|
### Missing Project Initialization
|
||||||
|
|
||||||
|
**Issue:** No package.json, build configuration, or source directories at project root
|
||||||
|
- Files: `package.json`, `tsconfig.json`, `src/` missing
|
||||||
|
- Impact: No standard development workflow established
|
||||||
|
- Fix approach: Run project initialization to create proper project structure
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hardware-Specific Concerns
|
||||||
|
|
||||||
|
### Low-Power Device Targeting
|
||||||
|
|
||||||
|
**Issue:** Target hardware is Raspberry Pi Zero 2W (limited resources)
|
||||||
|
- Device: Pi Zero 2 W with 512MB RAM, single-core ARMv8
|
||||||
|
- Concern: UI must be lightweight to run smoothly in Chromium Kiosk Mode
|
||||||
|
- Mitigation needed:
|
||||||
|
- Minimal JavaScript bundle size
|
||||||
|
- Efficient rendering (avoid heavy frameworks if possible)
|
||||||
|
- Consider static HTML/CSS over SPA for main interface
|
||||||
|
- Lazy-load any heavy components
|
||||||
|
|
||||||
|
### Dual Display Resolution Support
|
||||||
|
|
||||||
|
**Issue:** Must support two different display modes
|
||||||
|
- 7-inch touchscreen: 1024x600 resolution
|
||||||
|
- Full HD remote access: 1920x1080 resolution
|
||||||
|
- Concern: Responsive design complexity
|
||||||
|
- Mitigation needed:
|
||||||
|
- Clear responsive breakpoints
|
||||||
|
- Different layouts for touch vs desktop
|
||||||
|
- Performance testing on actual hardware
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requirements Complexity
|
||||||
|
|
||||||
|
### Feature Scope (3 Milestones)
|
||||||
|
|
||||||
|
**Issue:** Large feature set across three milestones
|
||||||
|
- Milestone 1: Modern compact UI
|
||||||
|
- Milestone 2: CSV processing and workflow
|
||||||
|
- Milestone 3: Network stack
|
||||||
|
- Concern: Scope creep and milestone creep
|
||||||
|
- Priority: High
|
||||||
|
|
||||||
|
### Data Types and Processing
|
||||||
|
|
||||||
|
**Issue:** Multiple sensor data types require handling
|
||||||
|
- Rainfall (today, hourly, MAR, yearly)
|
||||||
|
- Battery voltage
|
||||||
|
- Solar voltage
|
||||||
|
- Water level sensors (4-20mA, 0-10vDC)
|
||||||
|
- GPS coordinates
|
||||||
|
- Mobile network status
|
||||||
|
- Concern: Data normalization and storage complexity
|
||||||
|
|
||||||
|
### Network Protocol Support
|
||||||
|
|
||||||
|
**Issue:** Multiple transfer protocols specified in requirements
|
||||||
|
- FTP, SCP, SFTP, WEBDAV
|
||||||
|
- TCP socket connections
|
||||||
|
- Concern: Implementation complexity and security
|
||||||
|
- Mitigation: Start with simplest protocol (HTTP), add others as needed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies at Risk
|
||||||
|
|
||||||
|
### OpenCode Framework Dependency
|
||||||
|
|
||||||
|
**Issue:** Project configured to use OpenCode/GSD workflow
|
||||||
|
- Package: `@opencode-ai/plugin` v1.2.24
|
||||||
|
- Location: `.opencode/package.json`
|
||||||
|
- Risk: Vendor lock-in to specific workflow tool
|
||||||
|
- Mitigation: Document standard development practices that work independently
|
||||||
|
|
||||||
|
### Zod in node_modules
|
||||||
|
|
||||||
|
**Issue:** Zod library present in `.opencode/node_modules/`
|
||||||
|
- Purpose: Likely used for configuration validation
|
||||||
|
- Concern: Not yet integrated into project (in .)
|
||||||
|
- Recommendation: If validationopencode only needed, use zod; otherwise remove dependency
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Missing Infrastructure
|
||||||
|
|
||||||
|
### No Testing Framework
|
||||||
|
|
||||||
|
**Issue:** No test files or configuration detected
|
||||||
|
- No `jest.config.*`, `vitest.config.*`, or similar
|
||||||
|
- No `*.test.*` or `*.spec.*` files
|
||||||
|
- Impact: No test coverage for future implementation
|
||||||
|
- Priority: High - Testing should be established before implementation
|
||||||
|
|
||||||
|
### No Linting/Formatting
|
||||||
|
|
||||||
|
**Issue:** No ESLint, Prettier, or biome configuration
|
||||||
|
- Impact: Inconsistent code style as team grows
|
||||||
|
- Priority: Medium
|
||||||
|
|
||||||
|
### No CI/CD Configuration
|
||||||
|
|
||||||
|
**Issue:** No GitHub Actions, GitLab CI, or similar
|
||||||
|
- Impact: No automated testing or deployment
|
||||||
|
- Priority: Low (can be added later)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Considerations (Future)
|
||||||
|
|
||||||
|
### Network Credentials Storage
|
||||||
|
|
||||||
|
**Issue:** Requirements mention FTP/SCP credentials
|
||||||
|
- Files referenced: `THE_IDEA.md` lines 175-184
|
||||||
|
- Concern: Plain-text credentials in configuration
|
||||||
|
- Future mitigation:
|
||||||
|
- Use environment variables, not config files
|
||||||
|
- Consider OAuth/API keys over username/password
|
||||||
|
- Encrypt any stored credentials
|
||||||
|
|
||||||
|
### GPRS/Mobile Network
|
||||||
|
|
||||||
|
**Issue:** Mobile settings stored in system
|
||||||
|
- Location: `THE_IDEA.md` section 1.7
|
||||||
|
- Concern: Phone numbers and device IDs stored
|
||||||
|
- Future mitigation: Encrypt sensitive station data
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Performance Targets
|
||||||
|
|
||||||
|
### Startup Time
|
||||||
|
|
||||||
|
**Target:** Fast enough for Pi Zero 2W Chromium Kiosk
|
||||||
|
- Concern: Cold start must be under 3 seconds
|
||||||
|
- Mitigation: Minimize JavaScript, use progressive loading
|
||||||
|
|
||||||
|
### Memory Usage
|
||||||
|
|
||||||
|
**Target:** Stay within 256MB RAM for UI
|
||||||
|
- Concern: Chromium + web app + system services
|
||||||
|
- Mitigation: Avoid large in-memory data structures
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary of Priorities
|
||||||
|
|
||||||
|
| Concern | Priority | Status |
|
||||||
|
|---------|----------|--------|
|
||||||
|
| No source code exists | Critical | Not started |
|
||||||
|
| No testing framework | High | Not started |
|
||||||
|
| No project initialization | High | Not started |
|
||||||
|
| Hardware performance | High | Future |
|
||||||
|
| Requirements scope | Medium | Planned |
|
||||||
|
| No CI/CD | Low | Not started |
|
||||||
|
| Security hardening | Medium | Future |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Concerns audit: 2026-03-12*
|
||||||
239
.planning/codebase/CONVENTIONS.md
Normal file
239
.planning/codebase/CONVENTIONS.md
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# Coding Conventions
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-03-12
|
||||||
|
|
||||||
|
## Language
|
||||||
|
|
||||||
|
**Primary:** JavaScript (Node.js)
|
||||||
|
- All source files use CommonJS (`.cjs` extension)
|
||||||
|
- ESM imports used in test files with `.test.cjs` extension
|
||||||
|
|
||||||
|
## File Naming
|
||||||
|
|
||||||
|
**Pattern:** kebab-case
|
||||||
|
- Commands: `allow-read-config.cjs`, `get-profile.cjs`, `set-profile.cjs`
|
||||||
|
- Libraries: `oc-core.cjs`, `oc-config.cjs`, `oc-models.cjs`
|
||||||
|
- Tests: `get-profile.test.cjs`, `allow-read-config.test.cjs`
|
||||||
|
|
||||||
|
**Directories:**
|
||||||
|
- Commands: `bin/gsd-oc-commands/`
|
||||||
|
- Libraries: `bin/gsd-oc-lib/` or `bin/lib/`
|
||||||
|
- Tests: `bin/test/`
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
|
||||||
|
**No explicit formatter configured.** Code uses:
|
||||||
|
- 2-space indentation
|
||||||
|
- Consistent brace placement
|
||||||
|
- Max line length ~100 characters
|
||||||
|
|
||||||
|
**Example from `bin/gsd-oc-commands/get-profile.cjs`:**
|
||||||
|
```javascript
|
||||||
|
function getProfile(cwd, args) {
|
||||||
|
const verbose = args.includes('--verbose');
|
||||||
|
const raw = args.includes('--raw');
|
||||||
|
const log = verbose ? (...args) => console.error('[get-profile]', ...args) : () => {};
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
|
||||||
|
**No ESLint or Prettier configuration detected.**
|
||||||
|
|
||||||
|
## Import Organization
|
||||||
|
|
||||||
|
**Order in source files:**
|
||||||
|
1. Node.js built-ins (`fs`, `path`, `os`)
|
||||||
|
2. Local modules relative paths
|
||||||
|
|
||||||
|
**Example from `bin/gsd-oc-commands/allow-read-config.cjs`:**
|
||||||
|
```javascript
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const os = require('os');
|
||||||
|
const { output, error, createBackup } = require('../gsd-oc-lib/oc-core.cjs');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Function Design
|
||||||
|
|
||||||
|
### Naming
|
||||||
|
|
||||||
|
- Commands: lowercase with hyphens (e.g., `getProfile`, `allowReadConfig`)
|
||||||
|
- Helper functions: camelCase
|
||||||
|
- Error code constants: UPPER_SNAKE_CASE
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
**Every file includes a JSDoc-style header:**
|
||||||
|
```javascript
|
||||||
|
/**
|
||||||
|
* get-profile.cjs — Retrieve profile definitions from oc_config.json
|
||||||
|
*
|
||||||
|
* Command module that exports getProfile(cwd, args) function with two operation modes:
|
||||||
|
* 1. No parameters: Returns current profile definition
|
||||||
|
* 2. Profile name parameter: Returns specified profile definition
|
||||||
|
*
|
||||||
|
* Output format: JSON envelope {success: true, data: {...}}
|
||||||
|
* Flags: --raw (output raw JSON without envelope), --verbose (output diagnostics to stderr)
|
||||||
|
*
|
||||||
|
* Usage: node get-profile.cjs [profile-name] [--raw] [--verbose]
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Function JSDoc:**
|
||||||
|
```javascript
|
||||||
|
/**
|
||||||
|
* Main command function
|
||||||
|
*
|
||||||
|
* @param {string} cwd - Current working directory
|
||||||
|
* @param {string[]} args - Command line arguments
|
||||||
|
*/
|
||||||
|
function getProfile(cwd, args) {
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Error Codes
|
||||||
|
|
||||||
|
**Pattern:** Constant object with error codes, used with custom error function
|
||||||
|
|
||||||
|
From `bin/gsd-oc-lib/oc-profile-config.cjs`:
|
||||||
|
```javascript
|
||||||
|
const ERROR_CODES = {
|
||||||
|
CONFIG_NOT_FOUND: 'CONFIG_NOT_FOUND',
|
||||||
|
INVALID_JSON: 'INVALID_JSON',
|
||||||
|
PROFILE_NOT_FOUND: 'PROFILE_NOT_FOUND',
|
||||||
|
INVALID_MODELS: 'INVALID_MODELS',
|
||||||
|
INCOMPLETE_PROFILE: 'INCOMPLETE_PROFILE',
|
||||||
|
WRITE_FAILED: 'WRITE_FAILED',
|
||||||
|
APPLY_FAILED: 'APPLY_FAILED',
|
||||||
|
ROLLBACK_FAILED: 'ROLLBACK_FAILED'
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output Pattern
|
||||||
|
|
||||||
|
**Success output:** JSON envelope with `success: true` and `data`
|
||||||
|
```javascript
|
||||||
|
output({ success: true, data: result });
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error output:** Uses centralized `error()` function from `oc-core.cjs`
|
||||||
|
```javascript
|
||||||
|
error('current_oc_profile not set in oc_config.json', 'MISSING_CURRENT_PROFILE');
|
||||||
|
```
|
||||||
|
|
||||||
|
From `bin/gsd-oc-lib/oc-core.cjs`:
|
||||||
|
```javascript
|
||||||
|
function error(message, code = 'UNKNOWN_ERROR') {
|
||||||
|
const errorEnvelope = {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code,
|
||||||
|
message
|
||||||
|
}
|
||||||
|
};
|
||||||
|
console.error(JSON.stringify(errorEnvelope, null, 2));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Return Patterns
|
||||||
|
|
||||||
|
### Result Objects
|
||||||
|
|
||||||
|
**Pattern:** Return object with `success: boolean` and either `data` or `error`
|
||||||
|
|
||||||
|
From `bin/gsd-oc-lib/oc-profile-config.cjs`:
|
||||||
|
```javascript
|
||||||
|
function loadOcProfileConfig(cwd) {
|
||||||
|
try {
|
||||||
|
const configPath = path.join(cwd, '.planning', 'oc_config.json');
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: ERROR_CODES.CONFIG_NOT_FOUND,
|
||||||
|
message: `.planning/oc_config.json not found at ${configPath}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { success: true, config, configPath };
|
||||||
|
} catch (err) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Module Design
|
||||||
|
|
||||||
|
### Exports
|
||||||
|
|
||||||
|
**Pattern:** Single function export via `module.exports`
|
||||||
|
```javascript
|
||||||
|
module.exports = getProfile;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Command Module Structure
|
||||||
|
|
||||||
|
1. JSDoc header with description
|
||||||
|
2. Require statements
|
||||||
|
3. Error code constants
|
||||||
|
4. Helper functions
|
||||||
|
5. Main command function
|
||||||
|
6. `module.exports`
|
||||||
|
|
||||||
|
## CLI Patterns
|
||||||
|
|
||||||
|
### Flag Handling
|
||||||
|
|
||||||
|
**Pattern:** Simple array filtering
|
||||||
|
```javascript
|
||||||
|
const verbose = args.includes('--verbose');
|
||||||
|
const raw = args.includes('--raw');
|
||||||
|
const profileArgs = args.filter(arg => !arg.startsWith('--'));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output Functions
|
||||||
|
|
||||||
|
From `bin/gsd-oc-lib/oc-core.cjs`:
|
||||||
|
```javascript
|
||||||
|
function output(result, raw = false, rawValue = null) {
|
||||||
|
let outputStr;
|
||||||
|
if (raw && rawValue !== null) {
|
||||||
|
outputStr = rawValue;
|
||||||
|
} else {
|
||||||
|
outputStr = JSON.stringify(result, null, 2);
|
||||||
|
}
|
||||||
|
// Large payload handling (>50KB)
|
||||||
|
if (outputStr.length > 50 * 1024) {
|
||||||
|
const tempFile = path.join(require('os').tmpdir(), `gsd-oc-${Date.now()}.json`);
|
||||||
|
fs.writeFileSync(tempFile, outputStr, 'utf8');
|
||||||
|
console.log(`@file:${tempFile}`);
|
||||||
|
} else {
|
||||||
|
console.log(outputStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
### Verbose Logging Pattern
|
||||||
|
|
||||||
|
**Conditional logging based on flag:**
|
||||||
|
```javascript
|
||||||
|
const log = verbose ? (...args) => console.error('[get-profile]', ...args) : () => {};
|
||||||
|
log('Loading oc_config.json');
|
||||||
|
log(`Config loaded from ${configPath}`);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Conventions
|
||||||
|
|
||||||
|
Tests co-located in `bin/test/` directory with `.test.cjs` suffix.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Convention analysis: 2026-03-12*
|
||||||
190
.planning/codebase/INTEGRATIONS.md
Normal file
190
.planning/codebase/INTEGRATIONS.md
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
# External Integrations
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-03-12
|
||||||
|
|
||||||
|
## Project Status
|
||||||
|
|
||||||
|
This is a **greenfield project** - no implementation code exists yet. The following analysis is based on the requirements defined in `THE_IDEA.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hardware Interfaces
|
||||||
|
|
||||||
|
### Sensors
|
||||||
|
|
||||||
|
**Rainfall Sensors:**
|
||||||
|
- Rainfall-1 ID (RF) - Primary rainfall sensor (e.g., `123456RF`)
|
||||||
|
- Rainfall-2 ID - Secondary rainfall sensor
|
||||||
|
- Tip bucket counting for rainfall accumulation
|
||||||
|
|
||||||
|
**Water Level Sensors:**
|
||||||
|
- 4-20mA type sensors (Channels 1-2)
|
||||||
|
- 0-10vDC type sensors (Channels 3-4)
|
||||||
|
- SDI-12 interface for additional sensors
|
||||||
|
|
||||||
|
**Environmental Sensors:**
|
||||||
|
- Solar voltage monitoring
|
||||||
|
- Battery voltage monitoring
|
||||||
|
- Soil moisture sensors (SDI-12)
|
||||||
|
- Evapotranspiration sensors
|
||||||
|
|
||||||
|
### GPS Module
|
||||||
|
|
||||||
|
- USB GPS module for location data
|
||||||
|
- Latitude/longitude retrieval
|
||||||
|
- Manual coordinate setting as fallback
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Storage
|
||||||
|
|
||||||
|
### Local Storage
|
||||||
|
|
||||||
|
**CSV Files:**
|
||||||
|
- Data logging to local filesystem
|
||||||
|
- File naming: `SP8020_FTP.txt`, `SP8021_FTP.txt`, etc.
|
||||||
|
- Flash memory management for stored files
|
||||||
|
- File operations: Navigate, View, Delete, Save
|
||||||
|
|
||||||
|
**Data Types Logged:**
|
||||||
|
- Rainfall (today, hourly, monthly accumulated, yearly)
|
||||||
|
- Water levels
|
||||||
|
- Battery voltage
|
||||||
|
- Solar voltage
|
||||||
|
- Sensor readings (ADC channels)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Network & External Services
|
||||||
|
|
||||||
|
### Data Server (myvscada)
|
||||||
|
|
||||||
|
**Server Details:**
|
||||||
|
- Remote server for receiving CSV data
|
||||||
|
- Location: Configurable IP address
|
||||||
|
- Protocol: FTP, SCP, SFTP, or WEBDAV
|
||||||
|
|
||||||
|
**Transfer Flow:**
|
||||||
|
```
|
||||||
|
RTU → CSV file → myvscada server → received folder → processed/error folder
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mobile Network (GPRS)
|
||||||
|
|
||||||
|
**GPRS Module:**
|
||||||
|
- Mobile network connectivity for remote transmission
|
||||||
|
- RTU Mobile number configuration
|
||||||
|
- Referenced in "Mobile Setting" submenu (THE_IDEA.md section 1.7)
|
||||||
|
|
||||||
|
### Protocols Supported
|
||||||
|
|
||||||
|
| Protocol | Purpose | Reference |
|
||||||
|
|----------|---------|-----------|
|
||||||
|
| FTP | CSV file transfer | Primary protocol mentioned |
|
||||||
|
| SCP | Secure copy | Section 1.6 |
|
||||||
|
| SFTP | Secure FTP | Section 1.6 |
|
||||||
|
| WebDAV | Web-based file access | Section 1.6 |
|
||||||
|
| TCP Socket | Real-time data requests | Section 1.6 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Authentication & Identity
|
||||||
|
|
||||||
|
**Station Configuration:**
|
||||||
|
- Station ID (e.g., `D007`)
|
||||||
|
- Device version number (e.g., `v4.0.4`)
|
||||||
|
- Login status for remote access
|
||||||
|
|
||||||
|
**Network Credentials:**
|
||||||
|
- FTP/SCP username and password
|
||||||
|
- Referenced in THE_IDEA.md section 1.6 (Protocol Setting)
|
||||||
|
- **Note:** Credentials should be stored securely (env vars, not plain config)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Alert Systems
|
||||||
|
|
||||||
|
### SMS/Call Alerts
|
||||||
|
|
||||||
|
**Contact Types:**
|
||||||
|
- Master phones (2 numbers)
|
||||||
|
- SubMaster phones (2 numbers)
|
||||||
|
- Engineer phones (up to 10 numbers)
|
||||||
|
- RTU Mobile number
|
||||||
|
|
||||||
|
**Alert Triggers:**
|
||||||
|
- Danger rainfall threshold (configurable mm per hour)
|
||||||
|
- Warning rainfall threshold
|
||||||
|
- Water level thresholds (Danger, Warning, Alert, Normal)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Display Interfaces
|
||||||
|
|
||||||
|
### Local Touchscreen
|
||||||
|
|
||||||
|
- **Resolution:** 1024x600
|
||||||
|
- **Connection:** Direct video output (HDMI/CSI)
|
||||||
|
- **Browser:** Chromium Kiosk Mode
|
||||||
|
- **URL:** `http://pihostname:8080`
|
||||||
|
|
||||||
|
### Remote Web Access
|
||||||
|
|
||||||
|
- **Resolution:** Full HD (1920x1080)
|
||||||
|
- **Authentication:** Login required
|
||||||
|
- **URL:** `http://pihostname:9090/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communication Status
|
||||||
|
|
||||||
|
**Monitoring Data:**
|
||||||
|
- Communication status display (e.g., `0ASU/-113dBm(0%)`)
|
||||||
|
- Signal strength (dBm)
|
||||||
|
- Connection percentage
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment Configuration
|
||||||
|
|
||||||
|
### Required Configuration Items
|
||||||
|
|
||||||
|
**Network:**
|
||||||
|
- Local IP Address
|
||||||
|
- Subnet Mask
|
||||||
|
- Gateway
|
||||||
|
- DNS Server
|
||||||
|
- MAC Address
|
||||||
|
- Transfer Protocol (FTP/SCP/SFTP/WEBDAV)
|
||||||
|
|
||||||
|
**Server:**
|
||||||
|
- Server IP Address
|
||||||
|
- TCP Port
|
||||||
|
- File Directory (e.g., `/myvscada/stationA`)
|
||||||
|
|
||||||
|
**Sensors:**
|
||||||
|
- ADC channel types (4-20mA, 0-10vDC)
|
||||||
|
- Sensor datum offsets
|
||||||
|
- Sensor ranges and units
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Flow Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
Sensors (ADC, RF, WL)
|
||||||
|
↓
|
||||||
|
Python Backend (data collection)
|
||||||
|
↓
|
||||||
|
CSV File Generation
|
||||||
|
↓
|
||||||
|
Transfer Protocol (FTP/SCP/SFTP/WebDAV)
|
||||||
|
↓
|
||||||
|
myvscada Server
|
||||||
|
↓
|
||||||
|
Processed/Error folder handling
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Integration audit: 2026-03-12*
|
||||||
137
.planning/codebase/STACK.md
Normal file
137
.planning/codebase/STACK.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# Technology Stack
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-03-12
|
||||||
|
|
||||||
|
## Project Status
|
||||||
|
|
||||||
|
This is a **greenfield project** - no implementation code exists yet. The repository currently contains only:
|
||||||
|
- `THE_IDEA.md` - Requirements specification document
|
||||||
|
- `.opencode/` - OpenCode/GSD framework configuration
|
||||||
|
|
||||||
|
The following stack analysis is based on the requirements defined in `THE_IDEA.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Languages
|
||||||
|
|
||||||
|
**Primary:**
|
||||||
|
- **HTML/CSS/JavaScript** - Web-based UI for the 7-inch touchscreen and remote full HD interface
|
||||||
|
- Expected to run in Chromium Kiosk Mode on Raspberry Pi
|
||||||
|
- Must support dual resolution: 1024x600 (touchscreen) and 1920x1080 (remote)
|
||||||
|
|
||||||
|
**Secondary:**
|
||||||
|
- **Python** (likely) - Backend for sensor data collection, CSV processing, and network operations
|
||||||
|
- Raspberry Pi standard runtime
|
||||||
|
- Used for serial communication with sensors (ADC, rainfall sensors)
|
||||||
|
- CSV file generation and management
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Runtime
|
||||||
|
|
||||||
|
**Environment:**
|
||||||
|
- **Raspberry Pi OS** - Primary runtime on Pi Zero 2 W and Pi 3B
|
||||||
|
- Pi Zero 2 W: ARMv8 single-core, 512MB RAM
|
||||||
|
- Pi 3B: ARMv7 quad-core, 1GB RAM
|
||||||
|
|
||||||
|
**Browser:**
|
||||||
|
- **Chromium** - Kiosk mode for touchscreen interface
|
||||||
|
- Target URL: `http://pihostname:8080` (touchscreen)
|
||||||
|
- Target URL: `http://pihostname:9090/` (remote full HD)
|
||||||
|
|
||||||
|
**Package Manager:**
|
||||||
|
- Not yet established (pending project initialization)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Frameworks
|
||||||
|
|
||||||
|
**Core:**
|
||||||
|
- **Lightweight web framework** (TBD) - For serving the UI
|
||||||
|
- Options: Flask, FastAPI (Python), or simple HTTP server
|
||||||
|
- Must be performant on low-power Pi Zero 2 W
|
||||||
|
- **Frontend** - Plain HTML/CSS or lightweight framework
|
||||||
|
- Recommendation: Static HTML/CSS for main interface (performance)
|
||||||
|
- Avoid heavy SPA frameworks to minimize memory footprint
|
||||||
|
|
||||||
|
**Sensor Interfaces:**
|
||||||
|
- **Serial communication** - For ADC, rainfall sensors, GPS
|
||||||
|
- **SDI-12** - For soil moisture and evapotranspiration sensors (referenced in requirements)
|
||||||
|
|
||||||
|
**Data Handling:**
|
||||||
|
- **CSV processing** - For data logging and transfer (per flowchart in THE_IDEA.md)
|
||||||
|
|
||||||
|
**Build/Dev:**
|
||||||
|
- Not yet established (pending project initialization)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Dependencies
|
||||||
|
|
||||||
|
**Hardware Interfaces:**
|
||||||
|
- Serial port communication (Python pyserial or similar)
|
||||||
|
- ADC reading libraries
|
||||||
|
- GPS module support (USB)
|
||||||
|
|
||||||
|
**Network:**
|
||||||
|
- FTP client (for data transfer to myvscada server)
|
||||||
|
- SCP/SFTP client
|
||||||
|
- WebDAV client
|
||||||
|
- TCP socket libraries
|
||||||
|
|
||||||
|
**Data Processing:**
|
||||||
|
- CSV generation and parsing libraries
|
||||||
|
- Data validation utilities
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
**Network Settings:**
|
||||||
|
- Local IP configuration (static IP)
|
||||||
|
- Subnet mask, gateway, DNS
|
||||||
|
- MAC address
|
||||||
|
- Transfer protocol selection (FTP/SCP/SFTP/WEBDAV)
|
||||||
|
|
||||||
|
**Sensor Configuration:**
|
||||||
|
- ADC channels (4-20mA, 0-10vDC types)
|
||||||
|
- Rainfall sensor settings
|
||||||
|
- Water level thresholds
|
||||||
|
- Evapotranspiration settings
|
||||||
|
|
||||||
|
**Server Settings:**
|
||||||
|
- Server IP and TCP port
|
||||||
|
- Tide data hours
|
||||||
|
- GPS coordinates (manual or USB GPS)
|
||||||
|
|
||||||
|
**Mobile Settings:**
|
||||||
|
- Phone numbers for alerts (Master, SubMaster, Engineer contacts)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Platform Requirements
|
||||||
|
|
||||||
|
**Development:**
|
||||||
|
- Standard web development tools (editor, browser)
|
||||||
|
- Python development environment
|
||||||
|
- Git for version control
|
||||||
|
|
||||||
|
**Production:**
|
||||||
|
- Raspberry Pi Zero 2 W or 3B
|
||||||
|
- 7-inch capacitive touchscreen (1024x600)
|
||||||
|
- Chromium browser (Kiosk mode)
|
||||||
|
- Network connectivity (Ethernet or mobile/GPRS)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Performance Constraints
|
||||||
|
|
||||||
|
| Resource | Target | Concern |
|
||||||
|
|----------|--------|---------|
|
||||||
|
| RAM | <256MB for UI | Pi Zero 2 W has only 512MB total |
|
||||||
|
| Startup time | <3 seconds | Cold start in Chromium Kiosk |
|
||||||
|
| Bundle size | Minimal | Optimize for low-power device |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Stack analysis: 2026-03-12*
|
||||||
148
.planning/codebase/STRUCTURE.md
Normal file
148
.planning/codebase/STRUCTURE.md
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
# Codebase Structure
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-03-12
|
||||||
|
|
||||||
|
## Directory Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
/root/tckuiyo/
|
||||||
|
├── THE_IDEA.md # Project concept documentation
|
||||||
|
├── .gitignore # Git ignore rules
|
||||||
|
├── .bg-shell/ # Background shell config (manifest.json)
|
||||||
|
├── .gsd/ # GSD project data
|
||||||
|
│ └── milestones/ # Milestone definitions (empty)
|
||||||
|
├── .opencode/ # GSD Framework (main project code)
|
||||||
|
│ ├── package.json # Dependencies (OpenCode plugin)
|
||||||
|
│ ├── bun.lock # Dependency lockfile
|
||||||
|
│ ├── opencode.db # OpenCode database
|
||||||
|
│ ├── commands/ # User-facing commands
|
||||||
|
│ ├── agents/ # AI agent definitions
|
||||||
|
│ ├── get-shit-done/ # Workflows
|
||||||
|
│ │ └── workflows/ # Execution logic
|
||||||
|
│ ├── skills/ # Specialized skills
|
||||||
|
│ ├── rules/ # Behavior rules
|
||||||
|
│ ├── node_modules/ # Dependencies
|
||||||
|
│ └── .gitignore # OpenCode-specific ignores
|
||||||
|
├── .planning/ # Project planning output
|
||||||
|
│ └── codebase/ # Codebase analysis docs (where mappers write)
|
||||||
|
└── (empty directories for future use)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory Purposes
|
||||||
|
|
||||||
|
**`.opencode/`:**
|
||||||
|
- Purpose: Core GSD framework configuration
|
||||||
|
- Contains: Commands, agents, workflows, skills, rules
|
||||||
|
- Key files: All 33 command files, 12 agent files, 36 workflow files
|
||||||
|
|
||||||
|
**`.opencode/commands/gsd/`:**
|
||||||
|
- Purpose: User-invokable GSD commands
|
||||||
|
- Contains: Markdown command definitions with YAML frontmatter
|
||||||
|
- Key files: `gsd-new-project.md`, `gsd-map-codebase.md`, `gsd-plan-phase.md`, `gsd-execute-phase.md`
|
||||||
|
|
||||||
|
**`.opencode/agents/`:**
|
||||||
|
- Purpose: AI agent role definitions
|
||||||
|
- Contains: Agent configurations with tools and skills
|
||||||
|
- Key files: `gsd-codebase-mapper.md`, `gsd-planner.md`, `gsd-executor.md`, `gsd-verifier.md`
|
||||||
|
|
||||||
|
**`.opencode/get-shit-done/workflows/`:**
|
||||||
|
- Purpose: Execution logic for commands
|
||||||
|
- Contains: Step-by-step workflow instructions
|
||||||
|
- Key files: `map-codebase.md`, `new-project.md`, `plan-phase.md`, `execute-phase.md`
|
||||||
|
|
||||||
|
**`.opencode/skills/`:**
|
||||||
|
- Purpose: Specialized capability modules
|
||||||
|
- Contains: Skill definitions with scripts
|
||||||
|
- Key files: `gsd-oc-select-model/` (model selection skill)
|
||||||
|
|
||||||
|
**`.opencode/rules/`:**
|
||||||
|
- Purpose: Global behavior rules
|
||||||
|
- Contains: Rule definitions for agent behavior
|
||||||
|
- Key files: `gsd-oc-work-hard.md`
|
||||||
|
|
||||||
|
**`.planning/codebase/`:**
|
||||||
|
- Purpose: Output location for codebase mapper agents
|
||||||
|
- Contains: Analysis documents written by mapper agents
|
||||||
|
- Target files: STACK.md, INTEGRATIONS.md, ARCHITECTURE.md, STRUCTURE.md, CONVENTIONS.md, TESTING.md, CONCERNS.md
|
||||||
|
|
||||||
|
**`.gsd/milestones/`:**
|
||||||
|
- Purpose: Milestone definitions for projects
|
||||||
|
- Contains: Milestone JSON files (currently empty)
|
||||||
|
- Used by: new-milestone, complete-milestone commands
|
||||||
|
|
||||||
|
## Key File Locations
|
||||||
|
|
||||||
|
**Entry Points:**
|
||||||
|
- `.opencode/commands/gsd/gsd-*.md`: Command definitions invoked via `/gsd-*`
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- `.opencode/package.json`: Dependencies - `@opencode-ai/plugin: 1.2.24`
|
||||||
|
- `.opencode/bun.lock`: Dependency lockfile
|
||||||
|
- `.gitignore`: Excludes node_modules, .opencode/, .gsd/, .planning/, *.swp
|
||||||
|
|
||||||
|
**Core Logic:**
|
||||||
|
- All logic in `.opencode/` - this IS the application (OpenCode configuration)
|
||||||
|
|
||||||
|
**Testing:**
|
||||||
|
- No separate test directory - OpenCode handles testing via verify-work workflow
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Commands: `gsd-{command-name}.md` (kebab-case)
|
||||||
|
- Agents: `gsd-{agent-name}.md` (kebab-case)
|
||||||
|
- Workflows: `{workflow-name}.md` (kebab-case)
|
||||||
|
- Skills: `{skill-name}/` directory with SKILL.md inside
|
||||||
|
|
||||||
|
**Directories:**
|
||||||
|
- `.opencode/commands/gsd/`: GSD commands group
|
||||||
|
- `.opencode/agents/`: Agent definitions
|
||||||
|
- `.opencode/get-shit-done/workflows/`: Workflow definitions
|
||||||
|
- `.opencode/skills/`: Skill packages
|
||||||
|
- `.opencode/node_modules/`: npm dependencies
|
||||||
|
- `.planning/codebase/`: Codebase analysis outputs
|
||||||
|
|
||||||
|
## Where to Add New Code
|
||||||
|
|
||||||
|
**New Command:**
|
||||||
|
- Implementation: `.opencode/commands/gsd/gsd-{name}.md`
|
||||||
|
- Workflow: Add corresponding `.opencode/get-shit-done/workflows/{name}.md`
|
||||||
|
|
||||||
|
**New Agent:**
|
||||||
|
- Implementation: `.opencode/agents/gsd-{name}.md`
|
||||||
|
|
||||||
|
**New Workflow:**
|
||||||
|
- Implementation: `.opencode/get-shit-done/workflows/{name}.md`
|
||||||
|
- Referenced by: Command's `<execution_context>` tag
|
||||||
|
|
||||||
|
**New Skill:**
|
||||||
|
- Implementation: `.opencode/skills/{skill-name}/SKILL.md`
|
||||||
|
|
||||||
|
**Codebase Analysis Output:**
|
||||||
|
- Location: `.planning/codebase/` (written by gsd-codebase-mapper agents)
|
||||||
|
|
||||||
|
## Special Directories
|
||||||
|
|
||||||
|
**`.opencode/`:**
|
||||||
|
- Purpose: OpenCode project root - contains all project code
|
||||||
|
- Generated: No - this IS the codebase
|
||||||
|
- Committed: Yes - core project files
|
||||||
|
|
||||||
|
**`.opencode/node_modules/`:**
|
||||||
|
- Purpose: Dependencies for OpenCode plugin
|
||||||
|
- Generated: Yes (via bun install)
|
||||||
|
- Committed: No (in .gitignore)
|
||||||
|
|
||||||
|
**`.planning/`:**
|
||||||
|
- Purpose: Planning output and project state
|
||||||
|
- Generated: Yes (by GSD commands)
|
||||||
|
- Committed: Yes (project state is versioned)
|
||||||
|
|
||||||
|
**`.gsd/`:**
|
||||||
|
- Purpose: Project milestone and tracking data
|
||||||
|
- Generated: Yes (by GSD commands)
|
||||||
|
- Committed: Yes (project progress is versioned)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Structure analysis: 2026-03-12*
|
||||||
345
.planning/codebase/TESTING.md
Normal file
345
.planning/codebase/TESTING.md
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
# Testing Patterns
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-03-12
|
||||||
|
|
||||||
|
## Test Framework
|
||||||
|
|
||||||
|
**Mixed approach detected:**
|
||||||
|
|
||||||
|
### Primary: Node.js built-in `node:test`
|
||||||
|
- Used in `bin/gsd-tools.test.cjs`
|
||||||
|
- Version: Node.js built-in (no package needed)
|
||||||
|
- Config: None detected
|
||||||
|
|
||||||
|
### Secondary: Vitest
|
||||||
|
- Used in `bin/test/*.test.cjs` files
|
||||||
|
- Version: `^3.2.4` (from package.json)
|
||||||
|
- Config: None detected (uses defaults)
|
||||||
|
|
||||||
|
**Run Commands:**
|
||||||
|
```bash
|
||||||
|
npm test # Run all tests (vitest run)
|
||||||
|
npm run test:watch # Watch mode (vitest)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test File Organization
|
||||||
|
|
||||||
|
**Location:** `bin/test/`
|
||||||
|
|
||||||
|
**Naming:** `{command-name}.test.cjs`
|
||||||
|
|
||||||
|
**Example files:**
|
||||||
|
- `bin/test/get-profile.test.cjs`
|
||||||
|
- `bin/test/set-profile.test.cjs`
|
||||||
|
- `bin/test/allow-read-config.test.cjs`
|
||||||
|
- `bin/test/oc-profile-config.test.cjs`
|
||||||
|
- `bin/test/pivot-profile.test.cjs`
|
||||||
|
|
||||||
|
## Test Structure
|
||||||
|
|
||||||
|
### Vitest Style (Recommended)
|
||||||
|
|
||||||
|
From `bin/test/get-profile.test.cjs`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
|
describe('get-profile.cjs', () => {
|
||||||
|
let testDir;
|
||||||
|
let planningDir;
|
||||||
|
let configPath;
|
||||||
|
let capturedLog;
|
||||||
|
let capturedError;
|
||||||
|
let exitCode;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Create isolated test directory
|
||||||
|
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'get-profile-test-'));
|
||||||
|
planningDir = path.join(testDir, '.planning');
|
||||||
|
configPath = path.join(planningDir, 'oc_config.json');
|
||||||
|
fs.mkdirSync(planningDir, { recursive: true });
|
||||||
|
|
||||||
|
// Reset captured output
|
||||||
|
capturedLog = null;
|
||||||
|
capturedError = null;
|
||||||
|
exitCode = null;
|
||||||
|
|
||||||
|
// Mock console.log to capture all output
|
||||||
|
console.log = (msg) => {
|
||||||
|
allLogs.push(msg);
|
||||||
|
capturedLog = msg;
|
||||||
|
};
|
||||||
|
console.error = (msg) => {
|
||||||
|
allErrors.push(msg);
|
||||||
|
capturedError = msg;
|
||||||
|
};
|
||||||
|
process.exit = (code) => {
|
||||||
|
exitCode = code;
|
||||||
|
throw new Error(`process.exit(${code})`);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Restore original functions
|
||||||
|
console.log = originalLog;
|
||||||
|
console.error = originalError;
|
||||||
|
process.exit = originalExit;
|
||||||
|
|
||||||
|
// Cleanup test directory
|
||||||
|
try {
|
||||||
|
fs.rmSync(testDir, { recursive: true, force: true });
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore cleanup errors
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns current profile when current_oc_profile is set', () => {
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
||||||
|
const getProfile = importGetProfile();
|
||||||
|
|
||||||
|
try {
|
||||||
|
getProfile(testDir, []);
|
||||||
|
} catch (err) {
|
||||||
|
// Expected to throw due to process.exit mock
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
const output = JSON.parse(capturedLog);
|
||||||
|
expect(output.success).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Node:test Style
|
||||||
|
|
||||||
|
From `bin/gsd-tools.test.cjs`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const { test, describe, beforeEach, afterEach } = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
describe('history-digest command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('empty phases directory returns valid schema', () => {
|
||||||
|
const result = runGsdTools('history-digest', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const digest = JSON.parse(result.output);
|
||||||
|
assert.deepStrictEqual(digest.phases, {}, 'phases should be empty object');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Test Runner Style
|
||||||
|
|
||||||
|
From `bin/test/allow-read-config.test.cjs`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function testCreatePermission() {
|
||||||
|
console.log('Test: Create new opencode.json with permission...');
|
||||||
|
|
||||||
|
const testDir = createTestDir();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = runCLI(testDir, []);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(`Expected success, got: ${JSON.stringify(result)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify opencode.json was created
|
||||||
|
const opencodePath = path.join(testDir, 'opencode.json');
|
||||||
|
if (!fs.existsSync(opencodePath)) {
|
||||||
|
throw new Error('opencode.json was not created');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✓ PASS: Create permission\n');
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('✗ FAIL:', err.message, '\n');
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
cleanupTestDir(testDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTests() {
|
||||||
|
console.log('Running allow-read-config tests...\n');
|
||||||
|
|
||||||
|
const results = [
|
||||||
|
testCreatePermission(),
|
||||||
|
testIdempotency(),
|
||||||
|
testDryRun(),
|
||||||
|
];
|
||||||
|
|
||||||
|
const passed = results.filter(r => r).length;
|
||||||
|
console.log(`Results: ${passed}/${total} tests passed`);
|
||||||
|
|
||||||
|
if (passed === total) {
|
||||||
|
process.exit(0);
|
||||||
|
} else {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mocking Patterns
|
||||||
|
|
||||||
|
### Console/Process Mocking
|
||||||
|
|
||||||
|
**Vitest approach:**
|
||||||
|
```javascript
|
||||||
|
const originalLog = console.log;
|
||||||
|
const originalError = console.error;
|
||||||
|
const originalExit = process.exit;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
console.log = (msg) => {
|
||||||
|
allLogs.push(msg);
|
||||||
|
capturedLog = msg;
|
||||||
|
};
|
||||||
|
console.error = (msg) => {
|
||||||
|
allErrors.push(msg);
|
||||||
|
capturedError = msg;
|
||||||
|
};
|
||||||
|
process.exit = (code) => {
|
||||||
|
exitCode = code;
|
||||||
|
throw new Error(`process.exit(${code})`);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
console.log = originalLog;
|
||||||
|
console.error = originalError;
|
||||||
|
process.exit = originalExit;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Require Cache Clearing
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const importGetProfile = () => {
|
||||||
|
const modulePath = '../gsd-oc-commands/get-profile.cjs';
|
||||||
|
delete require.cache[require.resolve(modulePath)];
|
||||||
|
return require(modulePath);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Fixtures
|
||||||
|
|
||||||
|
**Location:** `bin/test/fixtures/`
|
||||||
|
|
||||||
|
**Pattern:** In-file constants
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const VALID_CONFIG_WITH_CURRENT = {
|
||||||
|
current_oc_profile: 'smart',
|
||||||
|
profiles: {
|
||||||
|
presets: {
|
||||||
|
simple: {
|
||||||
|
planning: 'bailian-coding-plan/qwen3.5-plus',
|
||||||
|
execution: 'bailian-coding-plan/qwen3.5-plus',
|
||||||
|
verification: 'bailian-coding-plan/qwen3.5-plus'
|
||||||
|
},
|
||||||
|
smart: { /* ... */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Helpers
|
||||||
|
|
||||||
|
### CLI Execution
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function runCLI(testDir, args) {
|
||||||
|
const cmd = `node ${TOOLS_PATH} allow-read-config ${args.join(' ')}`;
|
||||||
|
const output = execSync(cmd, { cwd: testDir, encoding: 'utf8' });
|
||||||
|
return JSON.parse(output);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Temp Directory Creation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function createTestDir() {
|
||||||
|
const testDir = path.join(os.tmpdir(), `gsd-oc-test-${Date.now()}`);
|
||||||
|
fs.mkdirSync(testDir, { recursive: true });
|
||||||
|
return testDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupTestDir(testDir) {
|
||||||
|
if (fs.existsSync(testDir)) {
|
||||||
|
fs.rmSync(testDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Assertion Patterns
|
||||||
|
|
||||||
|
### Vitest
|
||||||
|
```javascript
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
expect(output.success).toBe(true);
|
||||||
|
expect(output.data).toHaveProperty('smart');
|
||||||
|
expect(output.data.smart).toEqual(VALID_CONFIG_WITH_CURRENT.profiles.presets.smart);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Node.js assert
|
||||||
|
```javascript
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
assert.deepStrictEqual(digest.phases, {}, 'phases should be empty object');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coverage
|
||||||
|
|
||||||
|
**No coverage configuration detected.** No enforcement of coverage thresholds.
|
||||||
|
|
||||||
|
## Test Types
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
- Individual function testing
|
||||||
|
- Use mocking for dependencies
|
||||||
|
- Test isolated behavior
|
||||||
|
|
||||||
|
### Integration/CLI Tests
|
||||||
|
- Execute CLI commands via `execSync`
|
||||||
|
- Test with real temporary directories
|
||||||
|
- Verify file creation/state changes
|
||||||
|
|
||||||
|
### Patterns
|
||||||
|
- Test both success and error paths
|
||||||
|
- Test flag combinations (--verbose, --dry-run)
|
||||||
|
- Test idempotency (running same command twice)
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
### Inconsistent Test Frameworks
|
||||||
|
- Some tests use Vitest, others use Node.js built-in `node:test`
|
||||||
|
- Some tests use manual test runner (no framework)
|
||||||
|
- **Recommendation:** Standardize on one framework
|
||||||
|
|
||||||
|
### Missing Test Config
|
||||||
|
- No `vitest.config.js` found
|
||||||
|
- No `jest.config.js` found
|
||||||
|
- **Recommendation:** Add configuration file
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Testing analysis: 2026-03-12*
|
||||||
Reference in New Issue
Block a user