@syntesseraai/opencode-feature-factory 0.6.4 → 0.6.6
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/README.md +36 -95
- package/agents/{ff-building-chatgpt.md → ff-building-codex.md} +4 -4
- package/agents/{ff-planning-chatgpt.md → ff-planning-codex.md} +5 -5
- package/agents/{ff-reviewing-chatgpt.md → ff-reviewing-codex.md} +5 -5
- package/agents/pipeline.md +38 -498
- package/bin/ff-deploy.js +35 -1
- package/commands/pipeline/building/breakdown.md +17 -0
- package/commands/pipeline/building/implement-batch.md +16 -0
- package/commands/pipeline/building/run.md +19 -0
- package/commands/pipeline/building/validate-batch.md +16 -0
- package/commands/pipeline/complete.md +12 -0
- package/commands/pipeline/planning/gate.md +22 -0
- package/commands/pipeline/planning/run-codex.md +22 -0
- package/commands/pipeline/planning/run-gemini.md +21 -0
- package/commands/pipeline/planning/run-opus.md +21 -0
- package/commands/pipeline/planning/run.md +25 -0
- package/commands/pipeline/planning/synthesize.md +18 -0
- package/commands/pipeline/reviewing/gate.md +16 -0
- package/commands/pipeline/reviewing/run-codex.md +12 -0
- package/commands/pipeline/reviewing/run-gemini.md +11 -0
- package/commands/pipeline/reviewing/run-opus.md +11 -0
- package/commands/pipeline/reviewing/run.md +24 -0
- package/commands/pipeline/reviewing/synthesize.md +18 -0
- package/commands/pipeline/reviewing/triage.md +16 -0
- package/commands/pipeline/start.md +21 -0
- package/dist/index.js +9 -0
- package/dist/plugin-config.d.ts +49 -0
- package/dist/plugin-config.js +140 -0
- package/package.json +2 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Implement validated batches with Codex
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-building-codex
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Read `.feature-factory/agents/ff-building-gemini-batches-{UUID}.md` and implement tasks batch-by-batch.
|
|
8
|
+
|
|
9
|
+
For each task:
|
|
10
|
+
|
|
11
|
+
1. implement code changes
|
|
12
|
+
2. add/update tests
|
|
13
|
+
3. run lint/typecheck/tests for impacted scope
|
|
14
|
+
4. persist completion report to `.feature-factory/agents/ff-building-codex-{UUID}-T{N}.md`
|
|
15
|
+
|
|
16
|
+
This command performs implementation only; reviewing is triggered by `/pipeline/building/run` and by rework flow.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run build phase from approved plan
|
|
3
|
+
subtask: true
|
|
4
|
+
return:
|
|
5
|
+
- /pipeline/building/breakdown
|
|
6
|
+
- /pipeline/building/validate-batch
|
|
7
|
+
- /pipeline/building/implement-batch
|
|
8
|
+
- /pipeline/reviewing/run
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Run build phase from `.feature-factory/plans/plan-final-{UUID}.md`.
|
|
12
|
+
|
|
13
|
+
Rules:
|
|
14
|
+
|
|
15
|
+
1. Maintain dependency-safe batching.
|
|
16
|
+
2. Only parallelize tasks with no dependency edges.
|
|
17
|
+
3. Persist batch/task outputs to `.feature-factory/agents/`.
|
|
18
|
+
4. Implementation is Codex-only via `ff-building-codex` (model `openai/gpt-5.3-codex`).
|
|
19
|
+
5. Send completed batches into reviewing via `/pipeline/reviewing/run`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Validate task batches for architecture and dependencies
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-building-gemini
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Read `.feature-factory/agents/ff-building-opus-tasks-{UUID}.md` and create dependency-safe batches.
|
|
8
|
+
|
|
9
|
+
For each batch:
|
|
10
|
+
|
|
11
|
+
1. validate architecture and codebase fit
|
|
12
|
+
2. confirm or adjust file targets
|
|
13
|
+
3. flag architectural risks
|
|
14
|
+
4. mark whether tasks can run in parallel
|
|
15
|
+
|
|
16
|
+
Persist enriched batches to `.feature-factory/agents/ff-building-gemini-batches-{UUID}.md`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Finalize pipeline run and summarize outcomes
|
|
3
|
+
subtask: true
|
|
4
|
+
model: openai/gpt-5.4
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Finalize the pipeline run.
|
|
8
|
+
|
|
9
|
+
1. Summarize accepted plan, implemented tasks, review outcomes, and assumptions.
|
|
10
|
+
2. Keep plans and reviews for auditability.
|
|
11
|
+
3. Clear transient pipeline agent context files.
|
|
12
|
+
4. Return concise completion report to user.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Apply planning consensus gate
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-planning-codex
|
|
5
|
+
model: openai/gpt-5.4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Read `.feature-factory/plans/plan-consensus-{UUID}.md` and apply the planning gate exactly:
|
|
9
|
+
|
|
10
|
+
- `>=75`: APPROVED
|
|
11
|
+
- `50-74`: REWORK
|
|
12
|
+
- `<50`: BLOCKED
|
|
13
|
+
|
|
14
|
+
Actions:
|
|
15
|
+
|
|
16
|
+
1. If APPROVED, persist `.feature-factory/plans/plan-final-{UUID}.md` from the synthesized plan.
|
|
17
|
+
2. If REWORK, append divergence feedback to pipeline context so the next planning loop iteration re-plans with explicit deltas.
|
|
18
|
+
3. If BLOCKED, update pipeline context with explicit user-decision-needed status.
|
|
19
|
+
|
|
20
|
+
Always write a gate summary to pipeline context and include one status line in your final output:
|
|
21
|
+
|
|
22
|
+
`PLANNING_GATE=APPROVED|REWORK|BLOCKED`
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Generate Codex planning proposal
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-planning-codex
|
|
5
|
+
model: openai/gpt-5.3-codex
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Create a comprehensive implementation plan from the current pipeline requirements.
|
|
9
|
+
|
|
10
|
+
Requirements brief:
|
|
11
|
+
|
|
12
|
+
$ARGUMENTS
|
|
13
|
+
|
|
14
|
+
Persist plan content using `ff-plans-update` into `.feature-factory/plans/plan-codex-{UUID}.md`.
|
|
15
|
+
|
|
16
|
+
Include:
|
|
17
|
+
|
|
18
|
+
- requirements summary
|
|
19
|
+
- architecture validation for the proposed implementation approach
|
|
20
|
+
- ordered implementation steps with file targets
|
|
21
|
+
- risks and mitigations
|
|
22
|
+
- testing and validation strategy
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Generate Gemini planning proposal
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-planning-gemini
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Create a comprehensive implementation plan from the current pipeline requirements.
|
|
8
|
+
|
|
9
|
+
Requirements brief:
|
|
10
|
+
|
|
11
|
+
$ARGUMENTS
|
|
12
|
+
|
|
13
|
+
Persist plan content using `ff-plans-update` into `.feature-factory/plans/plan-gemini-{UUID}.md`.
|
|
14
|
+
|
|
15
|
+
Include:
|
|
16
|
+
|
|
17
|
+
- requirements summary
|
|
18
|
+
- architecture validation for the proposed implementation approach
|
|
19
|
+
- ordered implementation steps with file targets
|
|
20
|
+
- risks and mitigations
|
|
21
|
+
- testing and validation strategy
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Generate Opus planning proposal
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-planning-opus
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Create a comprehensive implementation plan from the current pipeline requirements.
|
|
8
|
+
|
|
9
|
+
Requirements brief:
|
|
10
|
+
|
|
11
|
+
$ARGUMENTS
|
|
12
|
+
|
|
13
|
+
Persist plan content using `ff-plans-update` into `.feature-factory/plans/plan-opus-{UUID}.md`.
|
|
14
|
+
|
|
15
|
+
Include:
|
|
16
|
+
|
|
17
|
+
- requirements summary
|
|
18
|
+
- architecture validation for the proposed implementation approach
|
|
19
|
+
- ordered implementation steps with file targets
|
|
20
|
+
- risks and mitigations
|
|
21
|
+
- testing and validation strategy
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Execute one planning iteration
|
|
3
|
+
subtask: true
|
|
4
|
+
model: openai/gpt-5.4
|
|
5
|
+
parallel:
|
|
6
|
+
- /pipeline/planning/run-opus {as:plan-opus}
|
|
7
|
+
- /pipeline/planning/run-gemini {as:plan-gemini}
|
|
8
|
+
- /pipeline/planning/run-codex {as:plan-codex}
|
|
9
|
+
return:
|
|
10
|
+
- /pipeline/planning/synthesize
|
|
11
|
+
- /pipeline/planning/gate
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Run one complete planning iteration for the active pipeline.
|
|
15
|
+
|
|
16
|
+
Requirements brief:
|
|
17
|
+
|
|
18
|
+
$ARGUMENTS
|
|
19
|
+
|
|
20
|
+
Rules:
|
|
21
|
+
|
|
22
|
+
1. Persist each planner output to `.feature-factory/plans/plan-{model}-{UUID}.md`.
|
|
23
|
+
2. Preserve prior planning artifacts for audit.
|
|
24
|
+
3. Each planning model must include architecture validation in its proposal.
|
|
25
|
+
4. Continue only through gate outcomes defined in `/pipeline/planning/gate`.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Synthesize planning consensus
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-planning-codex
|
|
5
|
+
model: openai/gpt-5.4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Read the latest planning artifacts (`plan-opus-{UUID}.md`, `plan-gemini-{UUID}.md`, `plan-codex-{UUID}.md`) and produce a consensus report.
|
|
9
|
+
|
|
10
|
+
Persist as `.feature-factory/plans/plan-consensus-{UUID}.md`.
|
|
11
|
+
|
|
12
|
+
Required output sections:
|
|
13
|
+
|
|
14
|
+
1. `CONSENSUS_SCORE` (0-100)
|
|
15
|
+
2. `AGREED_ELEMENTS`
|
|
16
|
+
3. `DIVERGENT_ELEMENTS`
|
|
17
|
+
4. `SYNTHESIZED_PLAN`
|
|
18
|
+
5. `OPEN_QUESTIONS`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Apply review approval gate
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-reviewing-codex
|
|
5
|
+
model: openai/gpt-5.4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Read latest synthesis report and apply gate exactly:
|
|
9
|
+
|
|
10
|
+
- APPROVED when confidence `>=95` and unresolved issues count is `0`
|
|
11
|
+
- REWORK when below threshold and iteration < 10
|
|
12
|
+
- ESCALATE when iteration == 10 and still not approved
|
|
13
|
+
|
|
14
|
+
Persist gate decision in pipeline context and output one status line:
|
|
15
|
+
|
|
16
|
+
`REVIEW_GATE=APPROVED|REWORK|ESCALATE`
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Independent Codex review
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-reviewing-codex
|
|
5
|
+
model: openai/gpt-5.3-codex
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Review the current task from triage brief for correctness, quality, architecture validity, testing, security, and edge cases.
|
|
9
|
+
|
|
10
|
+
Classify findings as critical/high/medium/low and include confidence score.
|
|
11
|
+
|
|
12
|
+
Persist to `.feature-factory/agents/ff-reviewing-codex-{UUID}-T{N}.md`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Independent Gemini review
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-reviewing-gemini
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Review the current task from triage brief for correctness, quality, architecture validity, testing, security, and edge cases.
|
|
8
|
+
|
|
9
|
+
Classify findings as critical/high/medium/low and include confidence score.
|
|
10
|
+
|
|
11
|
+
Persist to `.feature-factory/agents/ff-reviewing-gemini-{UUID}-T{N}.md`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Independent Opus review
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-reviewing-opus
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Review the current task from triage brief for correctness, quality, architecture validity, testing, security, and edge cases.
|
|
8
|
+
|
|
9
|
+
Classify findings as critical/high/medium/low and include confidence score.
|
|
10
|
+
|
|
11
|
+
Persist to `.feature-factory/agents/ff-reviewing-opus-{UUID}-T{N}.md`.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run review loop for completed tasks
|
|
3
|
+
subtask: true
|
|
4
|
+
model: openai/gpt-5.4
|
|
5
|
+
loop:
|
|
6
|
+
max: 10
|
|
7
|
+
until: all tasks in the active batch are approved with confidence >=95 and zero unresolved issues
|
|
8
|
+
return:
|
|
9
|
+
- /pipeline/reviewing/triage
|
|
10
|
+
- /pipeline/reviewing/run-opus {as:review-opus}
|
|
11
|
+
- /pipeline/reviewing/run-gemini {as:review-gemini}
|
|
12
|
+
- /pipeline/reviewing/run-codex {as:review-codex}
|
|
13
|
+
- /pipeline/reviewing/synthesize
|
|
14
|
+
- /pipeline/reviewing/gate
|
|
15
|
+
- If `REVIEW_GATE=REWORK`, invoke `/pipeline/building/implement-batch` to apply fixes before the next loop iteration.
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
Execute the review loop for the current completed tasks/rework report.
|
|
19
|
+
|
|
20
|
+
Stop criteria:
|
|
21
|
+
|
|
22
|
+
- approve when `>=95` confidence and zero unresolved issues
|
|
23
|
+
- continue loop while unresolved issues exist and iteration < 10
|
|
24
|
+
- escalate to user when iteration reaches 10 without approval
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Synthesize independent reviews into one report
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-reviewing-codex
|
|
5
|
+
model: openai/gpt-5.4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Read the latest three review reports (Opus, Gemini, and Codex) and synthesize a single authoritative output.
|
|
9
|
+
|
|
10
|
+
Required output:
|
|
11
|
+
|
|
12
|
+
1. overall confidence (0-100)
|
|
13
|
+
2. consolidated deduplicated findings
|
|
14
|
+
3. unresolved issues list
|
|
15
|
+
4. verdict `APPROVED` or `REWORK_REQUIRED`
|
|
16
|
+
5. explicit rework instructions when needed
|
|
17
|
+
|
|
18
|
+
Persist to `.feature-factory/agents/ff-reviewing-codex-synthesis-{UUID}-T{N}-iter{I}.md`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Build review brief from implementation outputs
|
|
3
|
+
subtask: true
|
|
4
|
+
agent: ff-reviewing-codex
|
|
5
|
+
model: openai/gpt-5.4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Prepare a review brief for the current completed task or rework output.
|
|
9
|
+
|
|
10
|
+
Include:
|
|
11
|
+
|
|
12
|
+
- summary of implemented changes
|
|
13
|
+
- files changed
|
|
14
|
+
- focus areas and risk points
|
|
15
|
+
|
|
16
|
+
Persist to `.feature-factory/agents/ff-reviewing-codex-triage-{UUID}-T{N}.md`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Pipeline workflow entrypoint
|
|
3
|
+
subtask: true
|
|
4
|
+
model: openai/gpt-5.4
|
|
5
|
+
return:
|
|
6
|
+
- /pipeline/planning/run {loop:5 && until:planning gate is APPROVED or planning gate is BLOCKED and waiting for user confirmation}
|
|
7
|
+
- If planning is BLOCKED or loop max was reached, stop and ask the user whether to continue planning iterations. Otherwise continue.
|
|
8
|
+
- /pipeline/building/run
|
|
9
|
+
- /pipeline/complete
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Start the deterministic Feature Factory pipeline with this requirements brief:
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
16
|
+
Execution requirements:
|
|
17
|
+
|
|
18
|
+
1. Create a pipeline UUID and persist orchestrator state in `.feature-factory/agents/pipeline-{UUID}.md` via `ff-agents-update`.
|
|
19
|
+
2. Keep all phase artifacts in `.feature-factory/plans/` and `.feature-factory/reviews/`.
|
|
20
|
+
3. Use command chaining, parallel fan-out, and loop gates from the `/pipeline/...` command tree.
|
|
21
|
+
4. Do not skip planning and review gates.
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { StopQualityGateHooksPlugin } from './stop-quality-gate.js';
|
|
2
2
|
import { updateMCPConfig } from './mcp-config.js';
|
|
3
3
|
import { updateAgentConfig } from './agent-config.js';
|
|
4
|
+
import { updatePluginConfig } from './plugin-config.js';
|
|
4
5
|
import { $ } from 'bun';
|
|
5
6
|
// Import tool creator functions
|
|
6
7
|
import { createFFAgentsCurrentTool } from './plugins/ff-agents-current-plugin.js';
|
|
@@ -53,6 +54,14 @@ export const FeatureFactoryPlugin = async (input) => {
|
|
|
53
54
|
catch {
|
|
54
55
|
console.error('Failed to update agent config in OpenCode plugin');
|
|
55
56
|
}
|
|
57
|
+
// Update plugin list in global OpenCode config
|
|
58
|
+
// This ensures required companion plugins are present
|
|
59
|
+
try {
|
|
60
|
+
await updatePluginConfig($);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
console.error('Failed to update plugin config in OpenCode plugin');
|
|
64
|
+
}
|
|
56
65
|
// Load hooks from the quality gate plugin
|
|
57
66
|
const qualityGateHooks = await StopQualityGateHooksPlugin(input).catch(() => ({}));
|
|
58
67
|
// Create all tools
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
type BunShell = any;
|
|
2
|
+
/**
|
|
3
|
+
* Default plugins that should be present in the global OpenCode config.
|
|
4
|
+
* These will be merged into the existing plugin array without removing
|
|
5
|
+
* any user-added entries.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_PLUGINS: readonly string[];
|
|
8
|
+
/**
|
|
9
|
+
* Extract the base package name from a plugin specifier, stripping the
|
|
10
|
+
* version suffix. Handles both scoped (`@scope/name@version`) and
|
|
11
|
+
* unscoped (`name@version`) packages.
|
|
12
|
+
*
|
|
13
|
+
* Examples:
|
|
14
|
+
* "@scope/pkg@latest" → "@scope/pkg"
|
|
15
|
+
* "@scope/pkg@1.2.3" → "@scope/pkg"
|
|
16
|
+
* "pkg@latest" → "pkg"
|
|
17
|
+
* "pkg" → "pkg"
|
|
18
|
+
*/
|
|
19
|
+
export declare function getPackageBaseName(plugin: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Check whether a plugin (by base name) is already present in the array,
|
|
22
|
+
* regardless of the version suffix. If a user has pinned a specific version
|
|
23
|
+
* (e.g. `@scope/pkg@1.2.3`), we treat the package as already present and
|
|
24
|
+
* do NOT add the `@latest` variant.
|
|
25
|
+
*/
|
|
26
|
+
export declare function hasPlugin(existing: string[], plugin: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Merge plugin arrays, preserving existing entries and appending new ones.
|
|
29
|
+
* Existing entries are never removed or reordered.
|
|
30
|
+
*
|
|
31
|
+
* Matching is done by base package name (without version suffix), so a
|
|
32
|
+
* user-pinned `@scope/pkg@1.0.0` will prevent `@scope/pkg@latest` from
|
|
33
|
+
* being added.
|
|
34
|
+
*/
|
|
35
|
+
export declare function mergePlugins(existing: string[] | undefined, defaults: readonly string[]): string[];
|
|
36
|
+
/**
|
|
37
|
+
* Update the plugin list in global opencode.json.
|
|
38
|
+
*
|
|
39
|
+
* This function:
|
|
40
|
+
* 1. Reads existing config from ~/.config/opencode/opencode.json
|
|
41
|
+
* 2. Preserves all existing plugins in the array
|
|
42
|
+
* 3. Appends default Feature Factory plugins that aren't already present
|
|
43
|
+
* (matched by base package name, so pinned versions are respected)
|
|
44
|
+
* 4. Writes updated config back to ~/.config/opencode/opencode.json
|
|
45
|
+
*
|
|
46
|
+
* @param $ - Bun shell instance
|
|
47
|
+
*/
|
|
48
|
+
export declare function updatePluginConfig($: BunShell): Promise<void>;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { readJsonFile } from './quality-gate-config.js';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const GLOBAL_OPENCODE_DIR = join(homedir(), '.config', 'opencode');
|
|
5
|
+
const GLOBAL_OPENCODE_CONFIG_PATH = join(GLOBAL_OPENCODE_DIR, 'opencode.json');
|
|
6
|
+
/**
|
|
7
|
+
* Default plugins that should be present in the global OpenCode config.
|
|
8
|
+
* These will be merged into the existing plugin array without removing
|
|
9
|
+
* any user-added entries.
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_PLUGINS = [
|
|
12
|
+
'@syntesseraai/opencode-feature-factory@latest',
|
|
13
|
+
'@nick-vi/opencode-type-inject@latest',
|
|
14
|
+
'@franlol/opencode-md-table-formatter@latest',
|
|
15
|
+
'@spoons-and-mirrors/subtask2@latest',
|
|
16
|
+
'opencode-pty@latest',
|
|
17
|
+
'@angdrew/opencode-hashline-plugin@latest',
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Extract the base package name from a plugin specifier, stripping the
|
|
21
|
+
* version suffix. Handles both scoped (`@scope/name@version`) and
|
|
22
|
+
* unscoped (`name@version`) packages.
|
|
23
|
+
*
|
|
24
|
+
* Examples:
|
|
25
|
+
* "@scope/pkg@latest" → "@scope/pkg"
|
|
26
|
+
* "@scope/pkg@1.2.3" → "@scope/pkg"
|
|
27
|
+
* "pkg@latest" → "pkg"
|
|
28
|
+
* "pkg" → "pkg"
|
|
29
|
+
*/
|
|
30
|
+
export function getPackageBaseName(plugin) {
|
|
31
|
+
if (plugin.startsWith('@')) {
|
|
32
|
+
// Scoped package: the version delimiter is the '@' after the '/'
|
|
33
|
+
const slashIndex = plugin.indexOf('/');
|
|
34
|
+
if (slashIndex !== -1) {
|
|
35
|
+
const versionAt = plugin.indexOf('@', slashIndex + 1);
|
|
36
|
+
if (versionAt !== -1) {
|
|
37
|
+
return plugin.substring(0, versionAt);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// No slash or no version suffix — return as-is
|
|
41
|
+
return plugin;
|
|
42
|
+
}
|
|
43
|
+
// Unscoped package: version delimiter is the first '@'
|
|
44
|
+
const atIndex = plugin.indexOf('@');
|
|
45
|
+
if (atIndex !== -1) {
|
|
46
|
+
return plugin.substring(0, atIndex);
|
|
47
|
+
}
|
|
48
|
+
return plugin;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check whether a plugin (by base name) is already present in the array,
|
|
52
|
+
* regardless of the version suffix. If a user has pinned a specific version
|
|
53
|
+
* (e.g. `@scope/pkg@1.2.3`), we treat the package as already present and
|
|
54
|
+
* do NOT add the `@latest` variant.
|
|
55
|
+
*/
|
|
56
|
+
export function hasPlugin(existing, plugin) {
|
|
57
|
+
const baseName = getPackageBaseName(plugin);
|
|
58
|
+
return existing.some((entry) => getPackageBaseName(entry) === baseName);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Merge plugin arrays, preserving existing entries and appending new ones.
|
|
62
|
+
* Existing entries are never removed or reordered.
|
|
63
|
+
*
|
|
64
|
+
* Matching is done by base package name (without version suffix), so a
|
|
65
|
+
* user-pinned `@scope/pkg@1.0.0` will prevent `@scope/pkg@latest` from
|
|
66
|
+
* being added.
|
|
67
|
+
*/
|
|
68
|
+
export function mergePlugins(existing, defaults) {
|
|
69
|
+
const existingPlugins = existing ?? [];
|
|
70
|
+
const result = [...existingPlugins];
|
|
71
|
+
for (const plugin of defaults) {
|
|
72
|
+
if (!hasPlugin(result, plugin)) {
|
|
73
|
+
result.push(plugin);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Update the plugin list in global opencode.json.
|
|
80
|
+
*
|
|
81
|
+
* This function:
|
|
82
|
+
* 1. Reads existing config from ~/.config/opencode/opencode.json
|
|
83
|
+
* 2. Preserves all existing plugins in the array
|
|
84
|
+
* 3. Appends default Feature Factory plugins that aren't already present
|
|
85
|
+
* (matched by base package name, so pinned versions are respected)
|
|
86
|
+
* 4. Writes updated config back to ~/.config/opencode/opencode.json
|
|
87
|
+
*
|
|
88
|
+
* @param $ - Bun shell instance
|
|
89
|
+
*/
|
|
90
|
+
export async function updatePluginConfig($) {
|
|
91
|
+
// Read existing global config
|
|
92
|
+
const globalJson = await readJsonFile($, GLOBAL_OPENCODE_CONFIG_PATH);
|
|
93
|
+
// Get existing plugins from global config
|
|
94
|
+
const existingPlugins = Array.isArray(globalJson?.plugin)
|
|
95
|
+
? globalJson.plugin
|
|
96
|
+
: undefined;
|
|
97
|
+
// Check if any changes are needed (by base name)
|
|
98
|
+
const existingArray = existingPlugins ?? [];
|
|
99
|
+
const hasChanges = DEFAULT_PLUGINS.some((plugin) => !hasPlugin(existingArray, plugin));
|
|
100
|
+
if (!hasChanges) {
|
|
101
|
+
// All default plugins already present (possibly with pinned versions)
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Merge with default plugins
|
|
105
|
+
const updatedPlugins = mergePlugins(existingPlugins, DEFAULT_PLUGINS);
|
|
106
|
+
// Prepare updated global config
|
|
107
|
+
const updatedGlobalConfig = {
|
|
108
|
+
...(globalJson ?? {}),
|
|
109
|
+
plugin: updatedPlugins,
|
|
110
|
+
};
|
|
111
|
+
// Ensure global config directory exists
|
|
112
|
+
try {
|
|
113
|
+
await $ `mkdir -p ${GLOBAL_OPENCODE_DIR}`.quiet();
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Directory might already exist, ignore
|
|
117
|
+
}
|
|
118
|
+
// Backup existing global config if it exists and has content
|
|
119
|
+
if (globalJson && Object.keys(globalJson).length > 0) {
|
|
120
|
+
try {
|
|
121
|
+
const timestamp = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
122
|
+
const backupPath = `${GLOBAL_OPENCODE_CONFIG_PATH}.backup.${timestamp}`;
|
|
123
|
+
const backupContent = JSON.stringify(globalJson, null, 2);
|
|
124
|
+
await $ `echo ${backupContent} > ${backupPath}`.quiet();
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
// Backup failed, but continue anyway
|
|
128
|
+
console.warn('[feature-factory] Could not create backup:', error);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Write updated config to global opencode.json
|
|
132
|
+
const configContent = JSON.stringify(updatedGlobalConfig, null, 2);
|
|
133
|
+
try {
|
|
134
|
+
await $ `echo ${configContent} > ${GLOBAL_OPENCODE_CONFIG_PATH}`.quiet();
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
// Silently fail - don't block if we can't write the config
|
|
138
|
+
console.warn('[feature-factory] Could not update plugin config:', error);
|
|
139
|
+
}
|
|
140
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@syntesseraai/opencode-feature-factory",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.6",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "OpenCode plugin for Feature Factory agents - provides sub-agents and skills for validation, review, security, and architecture assessment",
|
|
7
7
|
"license": "MIT",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"assets",
|
|
16
16
|
"skills",
|
|
17
17
|
"agents",
|
|
18
|
+
"commands",
|
|
18
19
|
"bin"
|
|
19
20
|
],
|
|
20
21
|
"keywords": [
|