Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .opencode/OPENCODE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ When you start in this directory, determine the installation state:
### If settings.json exists and is configured:
This is an established PAI installation. Read and follow the PAI skill:
```
read skills/PAI/SKILL.md
read PAI/SKILL.md
```

### If this is a fresh installation:
Expand All @@ -21,7 +21,7 @@ Welcome to PAI-OpenCode v1.0!

1. **Just start**: `opencode` - everything works with defaults
2. **Customize**: Edit `settings.json` for identity and preferences
3. **Learn more**: Read `skills/PAI/SKILL.md`
3. **Learn more**: Read `PAI/SKILL.md`

For full documentation, see the README.md in the repository root.

Expand Down
4 changes: 2 additions & 2 deletions .opencode/PAI/PAISYSTEMARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ PAI actions and pipelines run in two environments with identical behavior:
### Repository Separation

```
PRIVATE: ~/.claude/ PUBLIC: ${PROJECTS_DIR}/PAI/
PRIVATE: ~/.opencode/ PUBLIC: ${PROJECTS_DIR}/PAI/
├── Personal data ├── Sanitized examples
├── API keys (.env) ├── Generic templates
├── Session history └── Community sharing
Expand All @@ -487,7 +487,7 @@ The System skill is the centralized mechanism for PAI self-management. It ensure

| Function | Description | Workflow |
|----------|-------------|----------|
| **Integrity Audits** | 16 parallel agents verify broken references across ~/.claude | `PrivateSystemAudit.md` |
| **Integrity Audits** | 16 parallel agents verify broken references across ~/.opencode | `PrivateSystemAudit.md` |
| **Secret Scanning** | TruffleHog credential detection in any directory | `SecretScanning.md` |
| **Privacy Validation** | Ensures USER/WORK content isolation from regular skills | `PrivacyCheck.md` |
| **Cross-Repo Validation** | Verifies private/public repository separation | `CrossRepoValidation.md` |
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ Extended+: Rehearse verification for each CRITICAL criterion.

- Validate prerequisites: env vars, credentials, dependencies, state, files.
- Execution strategy: parallelize non-serial work at Extended+ (use Delegation skill).
- Create PRD at `~/.claude/MEMORY/WORK/{session-slug}/PRD-{YYYYMMDD}-{slug}.md` via `generatePRDTemplate()`.
- Create PRD at `${OPENCODE_DIR:-~/.opencode}/MEMORY/WORK/{session-slug}/PRD-{YYYYMMDD}-{slug}.md` via `generatePRDTemplate()` (writes to OPENCODE_DIR-based location).
- Write PLAN section. Every PRD requires a plan.
- For complex multi-approach tasks, use PlanMode skill.
- Quality Gate re-check.
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/THEDELEGATIONSYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Use the Agents skill to compose task-specific agents with unique traits, voices,

**For research specifically:** Use the Research skill, which has dedicated researcher agents (ClaudeResearcher, GeminiResearcher, etc.)

**Reference:** Agents skill (`~/.claude/skills/Agents/SKILL.md`)
**Reference:** Agents skill (`~/.opencode/skills/Agents/SKILL.md`)

**Full Context Requirements:**
When delegating, ALWAYS include:
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/AddBg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* add-bg input.png "#EAE9DF" output.png
* add-bg input.png --brand output.png
*
* @see ~/.claude/skills/Images/SKILL.md
* @see ~/.opencode/skills/Media/Art/SKILL.md
*/

import { existsSync } from "node:fs";
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/AlgorithmPhaseReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { join } from "path";
import { homedir } from "os";
import { parseArgs } from "util";

const STATE_DIR = join(homedir(), ".claude", "MEMORY", "STATE");
const STATE_DIR = join(process.env.OPENCODE_DIR || join(homedir(), ".opencode"), "MEMORY", "STATE");
const STATE_FILE = join(STATE_DIR, "algorithm-phase.json");

interface AlgorithmState {
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/Banner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { join } from "path";
import { spawnSync } from "child_process";

const HOME = process.env.HOME!;
const CLAUDE_DIR = join(HOME, ".claude");
const CLAUDE_DIR = process.env.OPENCODE_DIR || join(HOME, ".opencode");

// ═══════════════════════════════════════════════════════════════════════════
// Terminal Width Detection
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/BannerMatrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { spawnSync } from "child_process";
import { homedir } from "os";

const HOME = process.env.HOME ?? homedir();
const CLAUDE_DIR = join(HOME, ".claude");
const CLAUDE_DIR = process.env.OPENCODE_DIR || join(HOME, ".opencode");

// =============================================================================
// Terminal Width Detection
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/BannerNeofetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { join } from "path";
import { spawnSync } from "child_process";

const HOME = process.env.HOME!;
const CLAUDE_DIR = join(HOME, ".claude");
const CLAUDE_DIR = process.env.OPENCODE_DIR || join(HOME, ".opencode");

// ═══════════════════════════════════════════════════════════════════════
// Terminal Width Detection
Expand Down
39 changes: 26 additions & 13 deletions .opencode/PAI/Tools/BannerRetro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { join } from "path";
import { spawnSync } from "child_process";

const HOME = process.env.HOME!;
const CLAUDE_DIR = join(HOME, ".claude");
const OPENCODE_ROOT = process.env.OPENCODE_DIR || join(HOME, ".opencode");

// ═══════════════════════════════════════════════════════════════════════════
// Terminal Width Detection
Expand Down Expand Up @@ -311,7 +311,7 @@ interface SystemStats {
}

function readDAIdentity(): string {
const settingsPath = join(CLAUDE_DIR, "settings.json");
const settingsPath = join(OPENCODE_ROOT, "settings.json");
try {
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
return settings.daidentity?.displayName || settings.daidentity?.name || settings.env?.DA || "PAI";
Expand All @@ -321,7 +321,7 @@ function readDAIdentity(): string {
}

function countSkills(): number {
const skillsDir = join(CLAUDE_DIR, "skills");
const skillsDir = join(OPENCODE_ROOT, "skills");
if (!existsSync(skillsDir)) return 0;
let count = 0;
try {
Expand All @@ -333,7 +333,7 @@ function countSkills(): number {
}

function countUserFiles(): number {
const userDir = join(CLAUDE_DIR, "PAI/USER");
const userDir = join(OPENCODE_ROOT, "PAI/USER");
if (!existsSync(userDir)) return 0;
let count = 0;
const countRecursive = (dir: string) => {
Expand All @@ -349,19 +349,32 @@ function countUserFiles(): number {
}

function countHooks(): number {
const hooksDir = join(CLAUDE_DIR, "hooks");
if (!existsSync(hooksDir)) return 0;
const pluginsDir = join(OPENCODE_ROOT, "plugins");
const hooksDir = join(OPENCODE_ROOT, "hooks");

const targetDir = existsSync(pluginsDir) ? pluginsDir : hooksDir;
if (!existsSync(targetDir)) return 0;

let count = 0;
try {
for (const entry of readdirSync(hooksDir, { withFileTypes: true })) {
if (entry.isFile() && entry.name.endsWith(".ts")) count++;
}
} catch {}
const countRecursive = (dir: string) => {
try {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
countRecursive(fullPath);
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
count++;
}
}
} catch {}
};

countRecursive(targetDir);
return count;
}

function countWorkItems(): number {
const workDir = join(CLAUDE_DIR, "MEMORY/WORK");
const workDir = join(OPENCODE_ROOT, "MEMORY/WORK");
if (!existsSync(workDir)) return 0;
try {
return readdirSync(workDir, { withFileTypes: true })
Expand All @@ -372,7 +385,7 @@ function countWorkItems(): number {
}

function countLearnings(): number {
const learningDir = join(CLAUDE_DIR, "MEMORY/LEARNING");
const learningDir = join(OPENCODE_ROOT, "MEMORY/LEARNING");
if (!existsSync(learningDir)) return 0;
let count = 0;
const countRecursive = (dir: string) => {
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/FailureCapture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { join, basename } from 'path';
import { homedir } from 'os';
import { inference } from './Inference';

const PAI_DIR = process.env.PAI_DIR || join(process.env.HOME ?? homedir(), '.claude');
const PAI_DIR = process.env.OPENCODE_DIR || join(process.env.HOME ?? homedir(), '.opencode');

interface FailureCaptureInput {
transcriptPath: string;
Expand Down
9 changes: 6 additions & 3 deletions .opencode/PAI/Tools/FeatureRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* than Markdown because models are less likely to corrupt structured data.
*
* Usage:
* bun run ~/.claude/Tools/FeatureRegistry.ts <command> [options]
* bun run ~/.opencode/PAI/Tools/FeatureRegistry.ts <command> [options]
*
* Commands:
* init <project> Initialize feature registry for project
Expand All @@ -19,7 +19,7 @@
*/

import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { join } from 'path';
import { join, resolve } from 'path';
import { homedir } from 'os';

// Validate home directory
Expand All @@ -29,7 +29,10 @@ if (!HOME) {
process.exit(1);
}

const REGISTRY_DIR = join(HOME, '.claude', 'MEMORY', 'progress');
const OPENCODE_DIR = process.env.OPENCODE_DIR
? resolve(process.env.OPENCODE_DIR)
: join(HOME, '.opencode');
const REGISTRY_DIR = join(OPENCODE_DIR, 'MEMORY', 'progress');

interface TestStep {
step: string;
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/GenerateSkillIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Parses all SKILL.md files and builds a searchable index for dynamic skill discovery.
* Run this after adding/modifying skills to update the index.
*
* Usage: bun run ~/.opencode/skills/PAI/Tools/GenerateSkillIndex.ts
* Usage: bun run ~/.opencode/PAI/Tools/GenerateSkillIndex.ts
*
* Output: ~/.opencode/skills/skill-index.json
*/
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/GetCounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { readdirSync, existsSync, statSync } from "fs";
import { join } from "path";

const HOME = process.env.HOME!;
const PAI_DIR = process.env.PAI_DIR || join(HOME, ".claude");
const PAI_DIR = process.env.OPENCODE_DIR || join(HOME, ".opencode");

interface Counts {
skills: number;
Expand Down
9 changes: 5 additions & 4 deletions .opencode/PAI/Tools/IntegrityMaintenance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@ interface UpdateData {
// Constants
// ============================================================================

const PAI_DIR = process.env.HOME + '/.claude';
const CREATE_UPDATE_SCRIPT = join(PAI_DIR, 'skills/_SYSTEM/Tools/CreateUpdate.ts');
const OPENCODE_DIR = process.env.OPENCODE_DIR || process.env.HOME + '/.opencode';
const CREATE_UPDATE_SCRIPT = join(OPENCODE_DIR, 'skills/_SYSTEM/Tools/CreateUpdate.ts');
const UPDATE_SEARCH_SCRIPT = join(OPENCODE_DIR, 'PAI', 'Tools', 'UpdateSearch.ts');

// Words that indicate generic/bad titles - reject these
const GENERIC_TITLE_PATTERNS = [
Expand Down Expand Up @@ -719,7 +720,7 @@ async function generateVerboseNarrative(
future_impact: aiNarrative.future_impact,
future_bullets: aiNarrative.future_bullets,
verification_steps: aiNarrative.verification_steps,
verification_commands: [`bun ~/.claude/skills/_SYSTEM/Tools/UpdateSearch.ts recent 5`],
verification_commands: [`bun ${UPDATE_SEARCH_SCRIPT} recent 5`],
confidence: 'high',
},
aiTitle: aiNarrative.title,
Expand Down Expand Up @@ -749,7 +750,7 @@ async function generateVerboseNarrative(
future_impact: `The ${changeType.replace('_', ' ')} will use updated behavior.`,
future_bullets: ['Changes are active for future sessions'],
verification_steps: ['Changes applied via automatic detection'],
verification_commands: [`bun ~/.claude/skills/_SYSTEM/Tools/UpdateSearch.ts recent 5`],
verification_commands: [`bun ${UPDATE_SEARCH_SCRIPT} recent 5`],
confidence: 'medium',
},
};
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/LearningPatternSynthesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import * as path from "path";
// Configuration
// ============================================================================

const CLAUDE_DIR = path.join(process.env.HOME!, ".claude");
const CLAUDE_DIR = process.env.OPENCODE_DIR || path.join(process.env.HOME!, ".opencode");
const LEARNING_DIR = path.join(CLAUDE_DIR, "MEMORY", "LEARNING");
const RATINGS_FILE = path.join(LEARNING_DIR, "SIGNALS", "ratings.jsonl");
const SYNTHESIS_DIR = path.join(LEARNING_DIR, "SYNTHESIS");
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/LoadSkillConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ Usage:
bun LoadSkillConfig.ts --check <skill-name> Check if skill has customizations

Examples:
bun LoadSkillConfig.ts ~/.opencode/skills/PAIUpgrade sources.json
bun LoadSkillConfig.ts ~/.opencode/skills/Utilities/PAIUpgrade sources.json
bun LoadSkillConfig.ts --list
bun LoadSkillConfig.ts --check PAIUpgrade
`);
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/NeofetchBanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { join } from "path";
import { spawnSync } from "child_process";

const HOME = process.env.HOME!;
const CLAUDE_DIR = join(HOME, ".claude");
const CLAUDE_DIR = process.env.OPENCODE_DIR || join(HOME, ".opencode");

// ═══════════════════════════════════════════════════════════════════════
// Terminal Width Detection
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/OpinionTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { join } from 'path';

const PAI_DIR = process.env.PAI_DIR || join(process.env.HOME!, '.claude');
const PAI_DIR = process.env.OPENCODE_DIR || process.env.PAI_DIR || join(process.env.HOME!, '.opencode');
const OPINIONS_FILE = join(PAI_DIR, 'PAI/USER/OPINIONS.md');
const RELATIONSHIP_LOG = join(PAI_DIR, 'MEMORY/RELATIONSHIP');

Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/RelationshipReflect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync } from
import { join } from 'path';
import { execFileSync } from 'child_process';

const PAI_DIR = process.env.PAI_DIR || join(process.env.HOME!, '.claude');
const PAI_DIR = process.env.OPENCODE_DIR || join(process.env.HOME!, '.opencode');

interface RelationshipNote {
type: 'W' | 'B' | 'O';
Expand Down
2 changes: 1 addition & 1 deletion .opencode/PAI/Tools/RemoveBg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* remove-bg input.png output.png # Saves to new file
* remove-bg file1.png file2.png file3.png # Batch process
*
* @see ~/.claude/skills/Images/SKILL.md
* @see ~/.opencode/skills/Media/Art/SKILL.md
*/

import { readFile, writeFile } from "node:fs/promises";
Expand Down
16 changes: 7 additions & 9 deletions .opencode/PAI/Tools/SessionHarvester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* SessionHarvester - Extract learnings from Claude Code session transcripts
*
* Harvests insights from ~/.claude/projects/ sessions and writes to LEARNING/
* Harvests insights from ~/.opencode/projects/ sessions and writes to LEARNING/
*
* Commands:
* --recent N Harvest from N most recent sessions (default: 10)
Expand All @@ -19,19 +19,17 @@
import { parseArgs } from "util";
import * as fs from "fs";
import * as path from "path";
import { getLearningCategory, isLearningCapture } from "../../hooks/lib/learning-utils";
import { getLearningCategory, isLearningCapture } from "../../plugins/lib/learning-utils";

// ============================================================================
// Configuration
// ============================================================================

const CLAUDE_DIR = path.join(process.env.HOME!, ".claude");
// Derive the project slug dynamically from CLAUDE_DIR (works on macOS and Linux)
// macOS: ${HOME}/.claude → -Users-username--claude
// Linux: /home/username/.claude → -home-username--claude
const CWD_SLUG = CLAUDE_DIR.replace(/[\/\.]/g, "-");
const PROJECTS_DIR = path.join(CLAUDE_DIR, "projects", CWD_SLUG);
const LEARNING_DIR = path.join(CLAUDE_DIR, "MEMORY", "LEARNING");
const OPENCODE_DIR = process.env.OPENCODE_DIR || path.join(process.env.HOME!, ".opencode");
// Derive the project slug dynamically from current working directory
const CWD_SLUG = process.cwd().replace(/[\/\.]/g, "-");
const PROJECTS_DIR = path.join(OPENCODE_DIR, "projects", CWD_SLUG);
Comment on lines +28 to +31
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

CWD_SLUG derivation produces problematic path slugs.

The regex replace(/[\/\.]/g, "-") will produce slugs like -home-user-projects-myproject:

  • Leading dash from the root /
  • Multiple consecutive dashes from paths like /./
  • Not normalized or trimmed

This may cause inconsistent session directories and break the deterministic slug naming contract mentioned in SKILL.md.

Additionally, missing PAI_DIR fallback (same pattern as algorithm.ts).

🔧 Suggested fix for slug normalization
-const OPENCODE_DIR = process.env.OPENCODE_DIR || path.join(process.env.HOME!, ".opencode");
+const OPENCODE_DIR = process.env.OPENCODE_DIR || process.env.PAI_DIR || path.join(process.env.HOME!, ".opencode");
 // Derive the project slug dynamically from current working directory
-const CWD_SLUG = process.cwd().replace(/[\/\.]/g, "-");
+const CWD_SLUG = process.cwd()
+  .replace(/[\/\.]/g, "-")
+  .replace(/^-+/, "")         // Remove leading dashes
+  .replace(/-+/g, "-");       // Collapse consecutive dashes
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const OPENCODE_DIR = process.env.OPENCODE_DIR || path.join(process.env.HOME!, ".opencode");
// Derive the project slug dynamically from current working directory
const CWD_SLUG = process.cwd().replace(/[\/\.]/g, "-");
const PROJECTS_DIR = path.join(OPENCODE_DIR, "projects", CWD_SLUG);
const OPENCODE_DIR = process.env.OPENCODE_DIR || process.env.PAI_DIR || path.join(process.env.HOME!, ".opencode");
// Derive the project slug dynamically from current working directory
const CWD_SLUG = process.cwd()
.replace(/[\/\.]/g, "-")
.replace(/^-+/, "") // Remove leading dashes
.replace(/-+/g, "-"); // Collapse consecutive dashes
const PROJECTS_DIR = path.join(OPENCODE_DIR, "projects", CWD_SLUG);

const LEARNING_DIR = path.join(OPENCODE_DIR, "MEMORY", "LEARNING");

// Patterns indicating learning moments in conversations
const CORRECTION_PATTERNS = [
Expand Down
4 changes: 2 additions & 2 deletions .opencode/PAI/Tools/SessionProgress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Based on Anthropic's claude-progress.txt pattern.
*
* Usage:
* bun run ~/.claude/PAI/Tools/SessionProgress.ts <command> [options]
* bun run ~/.opencode/PAI/Tools/SessionProgress.ts <command> [options]
*/

import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync } from 'fs';
Expand Down Expand Up @@ -52,7 +52,7 @@ interface SessionProgress {
}

// Progress files are now in STATE/progress/ (consolidated from MEMORY/PROGRESS/)
const PROGRESS_DIR = join(HOME, '.claude', 'MEMORY', 'STATE', 'progress');
const PROGRESS_DIR = join(process.env.OPENCODE_DIR || join(HOME, '.opencode'), 'MEMORY', 'STATE', 'progress');

function getProgressPath(project: string): string {
// Validate project name to prevent path traversal
Expand Down
Loading
Loading