@duypham93/openkit 0.2.0
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/.opencode/README.md +47 -0
- package/.opencode/install-manifest.json +41 -0
- package/.opencode/lib/artifact-scaffolder.js +111 -0
- package/.opencode/lib/contract-consistency.js +218 -0
- package/.opencode/lib/parallel-execution-rules.js +261 -0
- package/.opencode/lib/runtime-paths.js +95 -0
- package/.opencode/lib/runtime-summary.js +82 -0
- package/.opencode/lib/state-guard.js +99 -0
- package/.opencode/lib/task-board-rules.js +375 -0
- package/.opencode/lib/work-item-store.js +280 -0
- package/.opencode/lib/workflow-state-controller.js +1739 -0
- package/.opencode/lib/workflow-state-rules.js +331 -0
- package/.opencode/opencode.json +93 -0
- package/.opencode/package.json +3 -0
- package/.opencode/tests/artifact-scaffolder.test.js +733 -0
- package/.opencode/tests/multi-work-item-runtime.test.js +369 -0
- package/.opencode/tests/parallel-execution-runtime.test.js +259 -0
- package/.opencode/tests/session-start-hook.test.js +357 -0
- package/.opencode/tests/state-guard.test.js +124 -0
- package/.opencode/tests/task-board-rules.test.js +204 -0
- package/.opencode/tests/work-item-store.test.js +380 -0
- package/.opencode/tests/workflow-behavior.test.js +149 -0
- package/.opencode/tests/workflow-contract-consistency.test.js +387 -0
- package/.opencode/tests/workflow-state-cli.test.js +1275 -0
- package/.opencode/tests/workflow-state-controller.test.js +1038 -0
- package/.opencode/work-items/feature-001/state.json +70 -0
- package/.opencode/work-items/index.json +13 -0
- package/.opencode/workflow-state.js +489 -0
- package/.opencode/workflow-state.json +70 -0
- package/AGENTS.md +265 -0
- package/README.md +401 -0
- package/agents/architect-agent.md +63 -0
- package/agents/ba-agent.md +56 -0
- package/agents/code-reviewer.md +77 -0
- package/agents/fullstack-agent.md +115 -0
- package/agents/master-orchestrator.md +60 -0
- package/agents/pm-agent.md +56 -0
- package/agents/qa-agent.md +124 -0
- package/agents/tech-lead-agent.md +60 -0
- package/assets/install-bundle/README.md +7 -0
- package/assets/install-bundle/opencode/README.md +11 -0
- package/assets/install-bundle/opencode/agents/ArchitectAgent.md +63 -0
- package/assets/install-bundle/opencode/agents/BAAgent.md +56 -0
- package/assets/install-bundle/opencode/agents/CodeReviewer.md +77 -0
- package/assets/install-bundle/opencode/agents/FullstackAgent.md +115 -0
- package/assets/install-bundle/opencode/agents/MasterOrchestrator.md +60 -0
- package/assets/install-bundle/opencode/agents/PMAgent.md +56 -0
- package/assets/install-bundle/opencode/agents/QAAgent.md +124 -0
- package/assets/install-bundle/opencode/agents/TechLeadAgent.md +60 -0
- package/assets/install-bundle/opencode/commands/brainstorm.md +44 -0
- package/assets/install-bundle/opencode/commands/delivery.md +45 -0
- package/assets/install-bundle/opencode/commands/execute-plan.md +44 -0
- package/assets/install-bundle/opencode/commands/migrate.md +61 -0
- package/assets/install-bundle/opencode/commands/quick-task.md +45 -0
- package/assets/install-bundle/opencode/commands/task.md +46 -0
- package/assets/install-bundle/opencode/commands/write-plan.md +50 -0
- package/assets/install-bundle/opencode/context/core/lane-selection.md +54 -0
- package/assets/install-bundle/opencode/skills/brainstorming/SKILL.md +51 -0
- package/assets/install-bundle/opencode/skills/code-review/SKILL.md +48 -0
- package/assets/install-bundle/opencode/skills/subagent-driven-development/SKILL.md +79 -0
- package/assets/install-bundle/opencode/skills/systematic-debugging/SKILL.md +61 -0
- package/assets/install-bundle/opencode/skills/test-driven-development/SKILL.md +48 -0
- package/assets/install-bundle/opencode/skills/using-skills/SKILL.md +39 -0
- package/assets/install-bundle/opencode/skills/verification-before-completion/SKILL.md +137 -0
- package/assets/install-bundle/opencode/skills/writing-plans/SKILL.md +68 -0
- package/assets/install-bundle/opencode/skills/writing-specs/SKILL.md +47 -0
- package/assets/opencode.json.template +11 -0
- package/assets/openkit-install.json.template +19 -0
- package/bin/openkit.js +9 -0
- package/commands/brainstorm.md +44 -0
- package/commands/delivery.md +45 -0
- package/commands/execute-plan.md +44 -0
- package/commands/migrate.md +61 -0
- package/commands/quick-task.md +45 -0
- package/commands/task.md +46 -0
- package/commands/write-plan.md +50 -0
- package/context/core/approval-gates.md +146 -0
- package/context/core/code-quality.md +42 -0
- package/context/core/issue-routing.md +85 -0
- package/context/core/lane-selection.md +54 -0
- package/context/core/project-config.md +143 -0
- package/context/core/session-resume.md +85 -0
- package/context/core/workflow-state-schema.md +224 -0
- package/context/core/workflow.md +442 -0
- package/context/navigation.md +94 -0
- package/docs/adr/README.md +6 -0
- package/docs/architecture/2026-03-20-task-intake-dashboard.md +54 -0
- package/docs/architecture/README.md +7 -0
- package/docs/briefs/2026-03-20-task-intake-dashboard.md +48 -0
- package/docs/briefs/README.md +7 -0
- package/docs/governance/README.md +25 -0
- package/docs/governance/adr-policy.md +27 -0
- package/docs/governance/definition-of-done.md +17 -0
- package/docs/governance/naming-conventions.md +21 -0
- package/docs/governance/severity-levels.md +12 -0
- package/docs/maintainer/README.md +51 -0
- package/docs/operations/README.md +79 -0
- package/docs/operations/internal-records/2026-03-24-release-checklist.md +79 -0
- package/docs/operations/internal-records/2026-03-24-simplified-install-ux.md +36 -0
- package/docs/operations/internal-records/README.md +18 -0
- package/docs/operations/runbooks/README.md +23 -0
- package/docs/operations/runbooks/openkit-daily-usage.md +288 -0
- package/docs/operations/runbooks/workflow-state-smoke-tests.md +302 -0
- package/docs/operator/README.md +50 -0
- package/docs/plans/2026-03-20-task-intake-dashboard.md +49 -0
- package/docs/plans/2026-03-21-openkit-full-delivery-multi-task-runtime.md +521 -0
- package/docs/plans/2026-03-23-openkit-global-install-runtime.md +157 -0
- package/docs/plans/README.md +7 -0
- package/docs/qa/2026-03-20-task-intake-dashboard.md +41 -0
- package/docs/qa/README.md +7 -0
- package/docs/specs/2026-03-20-task-intake-dashboard.md +50 -0
- package/docs/specs/2026-03-21-openkit-full-delivery-multi-task-runtime.md +462 -0
- package/docs/specs/README.md +7 -0
- package/docs/templates/README.md +36 -0
- package/docs/templates/adr-template.md +18 -0
- package/docs/templates/architecture-template.md +31 -0
- package/docs/templates/implementation-plan-template.md +32 -0
- package/docs/templates/migration-baseline-checklist.md +48 -0
- package/docs/templates/migration-plan-template.md +52 -0
- package/docs/templates/migration-report-template.md +74 -0
- package/docs/templates/migration-verify-checklist.md +39 -0
- package/docs/templates/product-brief-template.md +32 -0
- package/docs/templates/qa-report-template.md +37 -0
- package/docs/templates/quick-task-template.md +36 -0
- package/docs/templates/spec-template.md +31 -0
- package/hooks/hooks.json +16 -0
- package/hooks/session-start +162 -0
- package/package.json +24 -0
- package/registry.json +328 -0
- package/skills/brainstorming/SKILL.md +51 -0
- package/skills/code-review/SKILL.md +48 -0
- package/skills/subagent-driven-development/SKILL.md +79 -0
- package/skills/systematic-debugging/SKILL.md +61 -0
- package/skills/test-driven-development/SKILL.md +48 -0
- package/skills/using-skills/SKILL.md +39 -0
- package/skills/verification-before-completion/SKILL.md +137 -0
- package/skills/writing-plans/SKILL.md +68 -0
- package/skills/writing-specs/SKILL.md +47 -0
- package/src/audit/vietnamese-detection.js +259 -0
- package/src/cli/commands/detect-vietnamese.js +24 -0
- package/src/cli/commands/doctor.js +33 -0
- package/src/cli/commands/help.js +33 -0
- package/src/cli/commands/init.js +25 -0
- package/src/cli/commands/install-global.js +26 -0
- package/src/cli/commands/install.js +25 -0
- package/src/cli/commands/run.js +63 -0
- package/src/cli/commands/uninstall.js +32 -0
- package/src/cli/commands/upgrade.js +25 -0
- package/src/cli/conflict-output.js +19 -0
- package/src/cli/index.js +56 -0
- package/src/global/doctor.js +101 -0
- package/src/global/ensure-install.js +32 -0
- package/src/global/install-state.js +73 -0
- package/src/global/launcher.js +51 -0
- package/src/global/materialize.js +123 -0
- package/src/global/paths.js +85 -0
- package/src/global/uninstall.js +25 -0
- package/src/global/workspace-state.js +63 -0
- package/src/install/asset-manifest.js +284 -0
- package/src/install/conflicts.js +43 -0
- package/src/install/discovery.js +138 -0
- package/src/install/install-state.js +136 -0
- package/src/install/materialize.js +158 -0
- package/src/install/merge-policy.js +145 -0
- package/src/runtime/doctor.js +281 -0
- package/src/runtime/launcher.js +49 -0
- package/src/runtime/opencode-layering.js +135 -0
- package/src/runtime/openkit-managed-summary.js +27 -0
- package/tests/cli/openkit-cli.test.js +417 -0
- package/tests/global/doctor.test.js +130 -0
- package/tests/global/ensure-install.test.js +105 -0
- package/tests/install/discovery.test.js +124 -0
- package/tests/install/install-state.test.js +346 -0
- package/tests/install/materialize.test.js +244 -0
- package/tests/install/merge-policy.test.js +177 -0
- package/tests/runtime/doctor.test.js +430 -0
- package/tests/runtime/launcher.test.js +230 -0
- package/tests/runtime/module-boundary.test.js +16 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verification-before-completion
|
|
3
|
+
description: "Use before claiming work is complete, fixed, or passing. Requires fresh verification evidence before any success claim."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Verification Before Completion
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
Use this skill immediately before the agent:
|
|
11
|
+
|
|
12
|
+
- says the work is done
|
|
13
|
+
- claims tests pass / the fix is done / the workflow is complete
|
|
14
|
+
- creates a commit, PR, or merge
|
|
15
|
+
- moves the task to the next step as if it were already complete
|
|
16
|
+
|
|
17
|
+
OpenKit prioritizes **evidence before assertion**. Without fresh verification evidence, you must not speak as if the work is already sound.
|
|
18
|
+
|
|
19
|
+
## Iron Law
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
DO NOT CLAIM COMPLETION WITHOUT FRESH VERIFICATION EVIDENCE
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
If you have not run the command that proves the claim in the current working session, the agent must report the real status as "not yet verified" instead of guessing.
|
|
26
|
+
|
|
27
|
+
## Gate Function
|
|
28
|
+
|
|
29
|
+
Before making any statement that implies success:
|
|
30
|
+
|
|
31
|
+
1. IDENTIFY which command proves the claim
|
|
32
|
+
2. RUN the full command
|
|
33
|
+
3. READ the real output without inventing meaning
|
|
34
|
+
4. CHECK the exit code, error count, and pass/fail counts
|
|
35
|
+
5. ONLY THEN speak as if the outcome succeeded
|
|
36
|
+
|
|
37
|
+
If the command fails, or if no verification path exists yet, report that exact reality.
|
|
38
|
+
|
|
39
|
+
## What Counts as Valid Evidence
|
|
40
|
+
|
|
41
|
+
### Tests
|
|
42
|
+
|
|
43
|
+
Valid example:
|
|
44
|
+
|
|
45
|
+
- `node --test ".opencode/tests/*.test.js"` with real passing output
|
|
46
|
+
|
|
47
|
+
Not valid:
|
|
48
|
+
|
|
49
|
+
- "it passed earlier"
|
|
50
|
+
- "the code looks right"
|
|
51
|
+
- "part of the suite passed so the rest is probably fine"
|
|
52
|
+
|
|
53
|
+
### Runtime behavior
|
|
54
|
+
|
|
55
|
+
Valid examples:
|
|
56
|
+
|
|
57
|
+
- run `node .opencode/workflow-state.js status`
|
|
58
|
+
- run `node .opencode/workflow-state.js doctor`
|
|
59
|
+
- run a manual smoke test with clearly described observed input and output
|
|
60
|
+
|
|
61
|
+
Not valid:
|
|
62
|
+
|
|
63
|
+
- "this hook probably prints the right output because the unit test passed" when the claim is about integrated runtime behavior that has not been checked appropriately
|
|
64
|
+
|
|
65
|
+
### Requirements / plan completion
|
|
66
|
+
|
|
67
|
+
Valid example:
|
|
68
|
+
|
|
69
|
+
- compare every item in the brief, spec, or plan against the diff and verification output
|
|
70
|
+
|
|
71
|
+
Not valid:
|
|
72
|
+
|
|
73
|
+
- "tests passed, so the requirements must all be done"
|
|
74
|
+
|
|
75
|
+
## Current OpenKit Reality
|
|
76
|
+
|
|
77
|
+
OpenKit does not currently define repo-wide build, lint, or test commands for general application code.
|
|
78
|
+
|
|
79
|
+
So this skill must stay honest to the actual repo state:
|
|
80
|
+
|
|
81
|
+
- if the repo has workflow-runtime tests, use them
|
|
82
|
+
- if it only has runtime CLI checks or manual checks, say clearly that those are the real verification paths
|
|
83
|
+
- if no suitable validation path exists, report that gap instead of inventing a command
|
|
84
|
+
|
|
85
|
+
## Common Failure Patterns
|
|
86
|
+
|
|
87
|
+
| Claim | Required evidence | Not enough |
|
|
88
|
+
|------|-------------------|------------|
|
|
89
|
+
| "Tests pass" | latest test-command output | old run, memory, assumption |
|
|
90
|
+
| "Bug fixed" | symptom reproduction + passing verification | code changes alone |
|
|
91
|
+
| "Ready to commit" | passing verification for the relevant scope | only looking at the diff |
|
|
92
|
+
| "Requirements met" | checklist against spec/plan + verification | partial test success |
|
|
93
|
+
| "Agent task done" | inspect changes + verify behavior | trusting a subagent report |
|
|
94
|
+
|
|
95
|
+
## Red Flags
|
|
96
|
+
|
|
97
|
+
If you catch yourself thinking these phrases, stop:
|
|
98
|
+
|
|
99
|
+
- “should work now”
|
|
100
|
+
- “looks correct”
|
|
101
|
+
- “probably fine”
|
|
102
|
+
- “just this once”
|
|
103
|
+
- “the old test already passed”
|
|
104
|
+
- “I'm pretty sure”
|
|
105
|
+
|
|
106
|
+
Those are signs the agent is shifting from engineering into guessing.
|
|
107
|
+
|
|
108
|
+
## Required Output Style When Verification Fails or Is Missing
|
|
109
|
+
|
|
110
|
+
If verification fails:
|
|
111
|
+
|
|
112
|
+
- state the command you ran
|
|
113
|
+
- state the real failure status
|
|
114
|
+
- include the important output or summary
|
|
115
|
+
- do not use fuzzy wording like "almost done"
|
|
116
|
+
|
|
117
|
+
If no verification path exists:
|
|
118
|
+
|
|
119
|
+
- say clearly that the repo does not yet have an appropriate command
|
|
120
|
+
- describe any manual check you did perform
|
|
121
|
+
- explain the remaining limitation
|
|
122
|
+
|
|
123
|
+
## Before Commit / PR / Merge
|
|
124
|
+
|
|
125
|
+
Immediately before commit, PR, or merge, the agent must check:
|
|
126
|
+
|
|
127
|
+
- which claim is about to be made
|
|
128
|
+
- which command proves it
|
|
129
|
+
- whether fresh output actually exists
|
|
130
|
+
|
|
131
|
+
Do not use commit or merge as a way to "close the task and move on" when verification is still missing.
|
|
132
|
+
|
|
133
|
+
## Bottom Line
|
|
134
|
+
|
|
135
|
+
**Evidence before claims. Always.**
|
|
136
|
+
|
|
137
|
+
OpenKit may still lack tooling in many areas, but it must never lack honesty about verification status.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: writing-plans
|
|
3
|
+
description: "Converts approved architecture and scope into bite-sized implementation plans with validation matched to the workflow mode."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Writing Implementation Plans
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
This skill is used by the Tech Lead Agent. It turns design documents (spec and architecture) into concrete coding steps that the Fullstack Agent can execute.
|
|
11
|
+
|
|
12
|
+
Each plan should be detailed enough that the Fullstack Agent can execute it without guesswork.
|
|
13
|
+
|
|
14
|
+
## Core Rules of a Good Plan
|
|
15
|
+
|
|
16
|
+
1. **Bite-sized tasks**: each task should take roughly 2-5 minutes. If a task looks like more than 10 minutes, split it smaller.
|
|
17
|
+
2. **Atomic steps**: each step should be a complete, testable unit. Do not leave half-finished code behind.
|
|
18
|
+
3. **Exact file paths**: specify the exact absolute path or repository-relative path for every file to create or edit.
|
|
19
|
+
4. **Validation flow**: plan validation must match the active workflow mode. Full-delivery logic work should be TDD-first when the repository has suitable test tooling. Migration work should prioritize preserved invariants, blocker-decoupling steps, compatibility checks, staged verification, and targeted tests only where they are truly reliable and helpful. If the repo does not define a standard command yet, the plan must state the missing validation path instead of inventing one.
|
|
20
|
+
|
|
21
|
+
## Execution Process
|
|
22
|
+
|
|
23
|
+
### Step 1: Context Gathering
|
|
24
|
+
Make sure you have read:
|
|
25
|
+
- `docs/specs/YYYY-MM-DD-<feature>.md`
|
|
26
|
+
- `docs/architecture/YYYY-MM-DD-<feature>.md`
|
|
27
|
+
- `context/core/code-quality.md`
|
|
28
|
+
|
|
29
|
+
### Step 2: Write the Plan Document
|
|
30
|
+
|
|
31
|
+
Create `docs/plans/YYYY-MM-DD-<feature>.md` using this structure:
|
|
32
|
+
|
|
33
|
+
```markdown
|
|
34
|
+
# Implementation Plan: [Feature Name]
|
|
35
|
+
|
|
36
|
+
## Dependencies
|
|
37
|
+
- Are any additional packages required? (`npm install X`, `pip install Y`)
|
|
38
|
+
- Are any environment variables required?
|
|
39
|
+
|
|
40
|
+
## Implementation Steps
|
|
41
|
+
|
|
42
|
+
For each task, follow a validation-aware structure:
|
|
43
|
+
|
|
44
|
+
### [ ] Task 1: [Specific action name, e.g. Init Database Schema]
|
|
45
|
+
- **File**: `path/to/file.ext`
|
|
46
|
+
- **Goal**: [Brief description]
|
|
47
|
+
- **Validation Command**: `[test/build/typecheck/smoke/manual verification command for this step, or an explicit note that no repo-native validation command exists yet]`
|
|
48
|
+
- **Details**:
|
|
49
|
+
- State the baseline or expected change for this step
|
|
50
|
+
- State the implementation or upgrade action
|
|
51
|
+
- State how the result will be verified honestly
|
|
52
|
+
|
|
53
|
+
### [ ] Task 2: [Next task]
|
|
54
|
+
...
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Step 3: Review and Refine
|
|
58
|
+
- Are the tasks small enough?
|
|
59
|
+
- Does any task require changing more than 3 files at once? -> If yes, split it.
|
|
60
|
+
- In full mode, does any logic task skip the "write test first" step without justification? -> Add the test requirement.
|
|
61
|
+
- In migration mode, does the plan mix rewrite work into the migration instead of isolating blockers and proving parity? -> Rewrite the sequence.
|
|
62
|
+
- In migration mode, does the plan rely on fake TDD instead of baseline, compatibility, and regression evidence? -> Rewrite the validation guidance.
|
|
63
|
+
- Does the plan cover all acceptance criteria from the spec?
|
|
64
|
+
|
|
65
|
+
## Anti-Patterns
|
|
66
|
+
- "Task 1: Build the frontend, Task 2: Build the backend." (Far too large.)
|
|
67
|
+
- No test guidance or test command, and no explicit note that the repo lacks a standard command.
|
|
68
|
+
- A plan that does not specify which files need to be edited.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: writing-specs
|
|
3
|
+
description: "Converts requirements into structured spec documents with concrete acceptance criteria."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Writing Specs
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
This skill is used by the BA Agent to turn a high-level Product Brief into a detailed low-level spec that is ready for the Architect and the development team.
|
|
11
|
+
|
|
12
|
+
## Execution Process
|
|
13
|
+
|
|
14
|
+
### 1. Verification (Input Check)
|
|
15
|
+
- Make sure you already have a Product Brief.
|
|
16
|
+
- If the Product Brief is still vague (for example: "make it faster"), go back to the PM Agent or user and get measurable detail.
|
|
17
|
+
|
|
18
|
+
### 2. User Stories Breakdown
|
|
19
|
+
Break the feature into user flows (user stories).
|
|
20
|
+
**Required format:** `As a [User Type], I want [Action], so that [Benefit/Value].`
|
|
21
|
+
|
|
22
|
+
### 3. BDD Acceptance Criteria
|
|
23
|
+
This is the hardest and most important section. Each user story must include Given-When-Then acceptance criteria.
|
|
24
|
+
|
|
25
|
+
**Bad example (too vague):**
|
|
26
|
+
> The submit button should be disabled when the data is invalid.
|
|
27
|
+
|
|
28
|
+
**Good example (Given-When-Then):**
|
|
29
|
+
> **Given** the user is on the "Create New" form
|
|
30
|
+
> **And** the "Email" field is blank or invalid
|
|
31
|
+
> **When** they try to click the "Submit" button
|
|
32
|
+
> **Then** the "Submit" button must be disabled
|
|
33
|
+
> **And** an empty/invalid-format error message appears under the "Email" field
|
|
34
|
+
|
|
35
|
+
### 4. Edge Cases
|
|
36
|
+
You must include a dedicated section for failure conditions and awkward scenarios:
|
|
37
|
+
- What happens if the network drops mid-request?
|
|
38
|
+
- What happens if the user double-clicks the button?
|
|
39
|
+
- What happens with input that is too long, too short, or contains special characters?
|
|
40
|
+
- Race conditions?
|
|
41
|
+
|
|
42
|
+
### 5. Document Output
|
|
43
|
+
Create the markdown file at `docs/specs/YYYY-MM-DD-<feature-name>.md`.
|
|
44
|
+
|
|
45
|
+
## Anti-Patterns to Avoid
|
|
46
|
+
- **Tech leaking**: putting technical implementation decisions in the spec (for example: "Use React `useState` to store the form"). A spec should describe behavior and requirements, not code.
|
|
47
|
+
- **Unmeasurable goals**: "beautiful UI", "fast performance". Replace them with measurable requirements like "responsive on mobile" or "response time < 200ms".
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
import { OPENKIT_ASSET_MANIFEST } from '../install/asset-manifest.js';
|
|
6
|
+
|
|
7
|
+
const TEXT_EXTENSIONS = new Set(['.md', '.txt']);
|
|
8
|
+
const MACHINE_EXTENSIONS = new Set(['.json', '.js']);
|
|
9
|
+
const VIETNAMESE_DIACRITIC_REGEX = /[\u00C0-\u1EF9]/u;
|
|
10
|
+
const EXCLUDED_TOP_LEVEL_NAMES = new Set(['.git', '.worktrees', 'node_modules']);
|
|
11
|
+
const EXCLUDED_RELATIVE_PREFIXES = ['.git/', '.worktrees/', 'node_modules/'];
|
|
12
|
+
|
|
13
|
+
const HIGH_PRIORITY_PREFIXES = ['skills/', 'agents/', 'commands/', '.opencode/README.md'];
|
|
14
|
+
const MEDIUM_PRIORITY_PREFIXES = ['docs/', 'assets/install-bundle/opencode/'];
|
|
15
|
+
|
|
16
|
+
function walkFiles(rootPath, relativePath = '') {
|
|
17
|
+
const targetPath = relativePath ? path.join(rootPath, relativePath) : rootPath;
|
|
18
|
+
if (!fs.existsSync(targetPath)) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const stat = fs.statSync(targetPath);
|
|
23
|
+
if (stat.isFile()) {
|
|
24
|
+
return [relativePath || path.basename(targetPath)];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const files = [];
|
|
28
|
+
for (const entry of fs.readdirSync(targetPath, { withFileTypes: true })) {
|
|
29
|
+
const childRelativePath = relativePath ? path.join(relativePath, entry.name) : entry.name;
|
|
30
|
+
if (entry.isDirectory() && shouldExcludePath(childRelativePath)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
files.push(...walkFiles(rootPath, childRelativePath));
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (shouldExcludePath(childRelativePath)) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
files.push(childRelativePath);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return files;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function listTrackedFiles(projectRoot) {
|
|
47
|
+
const result = spawnSync('git', ['ls-files', '--cached', '--others', '--exclude-standard'], {
|
|
48
|
+
cwd: projectRoot,
|
|
49
|
+
encoding: 'utf8',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (result.status !== 0) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const trackedFiles = new Set();
|
|
57
|
+
|
|
58
|
+
for (const line of result.stdout.split('\n')) {
|
|
59
|
+
const normalizedPath = line.trim();
|
|
60
|
+
if (!normalizedPath) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const absolutePath = path.join(projectRoot, normalizedPath);
|
|
65
|
+
if (!fs.existsSync(absolutePath)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const stat = fs.statSync(absolutePath);
|
|
70
|
+
if (!stat.isFile()) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
trackedFiles.add(normalizedPath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return trackedFiles;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function listCheckedInFiles(projectRoot) {
|
|
81
|
+
const gitTrackedFiles = spawnSync('git', ['ls-files', '--cached'], {
|
|
82
|
+
cwd: projectRoot,
|
|
83
|
+
encoding: 'utf8',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (gitTrackedFiles.status === 0) {
|
|
87
|
+
const files = new Set();
|
|
88
|
+
|
|
89
|
+
for (const line of gitTrackedFiles.stdout.split('\n')) {
|
|
90
|
+
const normalizedPath = line.trim();
|
|
91
|
+
if (!normalizedPath) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const absolutePath = path.join(projectRoot, normalizedPath);
|
|
96
|
+
if (!fs.existsSync(absolutePath)) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const stat = fs.statSync(absolutePath);
|
|
101
|
+
if (!stat.isFile()) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
files.add(normalizedPath);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return files;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return new Set(walkFiles(projectRoot));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function shouldExcludePath(filePath) {
|
|
115
|
+
const normalizedPath = filePath.split(path.sep).join('/');
|
|
116
|
+
if (EXCLUDED_TOP_LEVEL_NAMES.has(normalizedPath)) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return EXCLUDED_RELATIVE_PREFIXES.some(
|
|
121
|
+
(prefix) => normalizedPath === prefix.slice(0, -1) || normalizedPath.startsWith(prefix)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function isTextCandidate(filePath) {
|
|
126
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
127
|
+
return TEXT_EXTENSIONS.has(extension) || MACHINE_EXTENSIONS.has(extension);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function isMachineFacing(filePath) {
|
|
131
|
+
return MACHINE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function readUtf8(projectRoot, relativePath) {
|
|
135
|
+
return fs.readFileSync(path.join(projectRoot, relativePath), 'utf8');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function classifyPriority(filePath) {
|
|
139
|
+
if (HIGH_PRIORITY_PREFIXES.some((prefix) => filePath === prefix || filePath.startsWith(prefix))) {
|
|
140
|
+
return 'high';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (MEDIUM_PRIORITY_PREFIXES.some((prefix) => filePath === prefix || filePath.startsWith(prefix))) {
|
|
144
|
+
return 'medium';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return 'low';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function buildPairingMap() {
|
|
151
|
+
const derivedPairs = OPENKIT_ASSET_MANIFEST.bundle.assets
|
|
152
|
+
.filter((asset) => asset.sourcePath !== asset.bundledPath)
|
|
153
|
+
.map((asset) => ({
|
|
154
|
+
sourcePath: asset.sourcePath,
|
|
155
|
+
derivedPath: asset.bundledPath,
|
|
156
|
+
assetId: asset.id,
|
|
157
|
+
derived: true,
|
|
158
|
+
}));
|
|
159
|
+
|
|
160
|
+
derivedPairs.push({
|
|
161
|
+
sourcePath: '.opencode/README.md',
|
|
162
|
+
derivedPath: 'assets/install-bundle/opencode/README.md',
|
|
163
|
+
assetId: 'opencode.bundle.runtime-readme',
|
|
164
|
+
derived: true,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return derivedPairs.sort((left, right) => left.sourcePath.localeCompare(right.sourcePath));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function detectVietnameseInventory(projectRoot) {
|
|
171
|
+
const filesToScan = listCheckedInFiles(projectRoot);
|
|
172
|
+
|
|
173
|
+
const textFiles = [...filesToScan].filter(isTextCandidate).sort((left, right) => left.localeCompare(right));
|
|
174
|
+
const humanFacingMatches = [];
|
|
175
|
+
const machineFacingMatches = [];
|
|
176
|
+
|
|
177
|
+
for (const filePath of textFiles) {
|
|
178
|
+
const contents = readUtf8(projectRoot, filePath);
|
|
179
|
+
if (!VIETNAMESE_DIACRITIC_REGEX.test(contents)) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const match = {
|
|
184
|
+
path: filePath,
|
|
185
|
+
priority: classifyPriority(filePath),
|
|
186
|
+
machineFacing: isMachineFacing(filePath),
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
if (match.machineFacing) {
|
|
190
|
+
machineFacingMatches.push(match);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
humanFacingMatches.push(match);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const pairingMap = buildPairingMap();
|
|
198
|
+
const matchedPaths = new Set(humanFacingMatches.map((match) => match.path));
|
|
199
|
+
const pairingSummary = pairingMap.map((pair) => ({
|
|
200
|
+
...pair,
|
|
201
|
+
sourceHasVietnamese: matchedPaths.has(pair.sourcePath),
|
|
202
|
+
derivedHasVietnamese: matchedPaths.has(pair.derivedPath),
|
|
203
|
+
}));
|
|
204
|
+
|
|
205
|
+
const priorityCounts = {
|
|
206
|
+
high: humanFacingMatches.filter((match) => match.priority === 'high').length,
|
|
207
|
+
medium: humanFacingMatches.filter((match) => match.priority === 'medium').length,
|
|
208
|
+
low: humanFacingMatches.filter((match) => match.priority === 'low').length,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
detectionMode: 'heuristic',
|
|
213
|
+
detectionScope: 'repo-wide checked-in files',
|
|
214
|
+
explicitExclusions: [...EXCLUDED_TOP_LEVEL_NAMES],
|
|
215
|
+
scannedFileCount: textFiles.length,
|
|
216
|
+
vietnameseBearingFiles: humanFacingMatches,
|
|
217
|
+
machineFacingMatches,
|
|
218
|
+
machineFacingOutOfScope: machineFacingMatches.length === 0,
|
|
219
|
+
priorityCounts,
|
|
220
|
+
pairingMap: pairingSummary,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function renderVietnameseInventoryReport(inventory) {
|
|
225
|
+
const lines = [];
|
|
226
|
+
lines.push(`Inventory status: ${inventory.vietnameseBearingFiles.length > 0 ? 'matches-found' : 'clear'}`);
|
|
227
|
+
lines.push(`Detection scope: ${inventory.detectionScope}`);
|
|
228
|
+
lines.push(`Detection mode: ${inventory.detectionMode}`);
|
|
229
|
+
lines.push('Heuristic review: required for false positives and false negatives');
|
|
230
|
+
lines.push(`Scanned files: ${inventory.scannedFileCount}`);
|
|
231
|
+
lines.push(
|
|
232
|
+
`Priority counts: high=${inventory.priorityCounts.high}, medium=${inventory.priorityCounts.medium}, low=${inventory.priorityCounts.low}`
|
|
233
|
+
);
|
|
234
|
+
lines.push(`Explicit exclusions: ${inventory.explicitExclusions.join(', ')}`);
|
|
235
|
+
lines.push(
|
|
236
|
+
`Machine-facing literals: ${inventory.machineFacingOutOfScope ? 'out-of-scope confirmed' : 'review needed'}`
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const completePairingCoverage = inventory.pairingMap.every(
|
|
240
|
+
(pair) => pair.sourceHasVietnamese === pair.derivedHasVietnamese
|
|
241
|
+
);
|
|
242
|
+
lines.push(`Pairing map coverage: ${completePairingCoverage ? 'complete' : 'mismatch-detected'}`);
|
|
243
|
+
lines.push('');
|
|
244
|
+
lines.push('Vietnamese-bearing checked-in files:');
|
|
245
|
+
|
|
246
|
+
for (const match of inventory.vietnameseBearingFiles) {
|
|
247
|
+
lines.push(`- [${match.priority}] ${match.path}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
lines.push('');
|
|
251
|
+
lines.push('Source-versus-derived pairing map:');
|
|
252
|
+
for (const pair of inventory.pairingMap) {
|
|
253
|
+
lines.push(
|
|
254
|
+
`- ${pair.sourcePath} -> ${pair.derivedPath} [source=${pair.sourceHasVietnamese ? 'vi' : 'clear'}, derived=${pair.derivedHasVietnamese ? 'vi' : 'clear'}]`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return `${lines.join('\n')}\n`;
|
|
259
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { detectVietnameseInventory, renderVietnameseInventoryReport } from '../../audit/vietnamese-detection.js';
|
|
2
|
+
|
|
3
|
+
function detectVietnameseHelp() {
|
|
4
|
+
return [
|
|
5
|
+
'Usage: openkit internal-audit-vietnamese',
|
|
6
|
+
'',
|
|
7
|
+
'Run the maintainer audit helper for heuristic Vietnamese detection across repo-wide checked-in files.',
|
|
8
|
+
'This helper uses heuristics and still requires review for false positives and false negatives.',
|
|
9
|
+
].join('\n');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const detectVietnameseCommand = {
|
|
13
|
+
name: 'internal-audit-vietnamese',
|
|
14
|
+
async run(args = [], io) {
|
|
15
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
16
|
+
io.stdout.write(`${detectVietnameseHelp()}\n`);
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const inventory = detectVietnameseInventory(process.cwd());
|
|
21
|
+
io.stdout.write(renderVietnameseInventoryReport(inventory));
|
|
22
|
+
return 0;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { inspectGlobalDoctor, renderGlobalDoctorSummary } from '../../global/doctor.js';
|
|
2
|
+
|
|
3
|
+
function doctorHelp() {
|
|
4
|
+
return [
|
|
5
|
+
'Usage: openkit doctor',
|
|
6
|
+
'',
|
|
7
|
+
'Check whether the global OpenKit install and the current workspace are ready.',
|
|
8
|
+
].join('\n');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const doctorCommand = {
|
|
12
|
+
name: 'doctor',
|
|
13
|
+
async run(args = [], io) {
|
|
14
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
15
|
+
io.stdout.write(`${doctorHelp()}\n`);
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const result = inspectGlobalDoctor({
|
|
20
|
+
projectRoot: process.cwd(),
|
|
21
|
+
env: process.env,
|
|
22
|
+
});
|
|
23
|
+
const output = renderGlobalDoctorSummary(result);
|
|
24
|
+
|
|
25
|
+
if (result.canRunCleanly) {
|
|
26
|
+
io.stdout.write(output);
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
io.stdout.write(output);
|
|
31
|
+
return 1;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
function topLevelHelp() {
|
|
2
|
+
return [
|
|
3
|
+
'Usage: openkit <command> [options]',
|
|
4
|
+
'',
|
|
5
|
+
'Quickstart:',
|
|
6
|
+
' npm install -g openkit',
|
|
7
|
+
' openkit run',
|
|
8
|
+
'',
|
|
9
|
+
'Commands:',
|
|
10
|
+
' help Show CLI help',
|
|
11
|
+
' install-global Manual global setup command',
|
|
12
|
+
' init Compatibility alias for install-global',
|
|
13
|
+
' install Compatibility alias for install-global',
|
|
14
|
+
' run Launch OpenCode and perform first-time setup if needed',
|
|
15
|
+
' upgrade Refresh the global OpenKit install',
|
|
16
|
+
' uninstall Remove the global OpenKit install',
|
|
17
|
+
' doctor Inspect global OpenKit and workspace readiness',
|
|
18
|
+
].join('\n');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const helpCommand = {
|
|
22
|
+
name: 'help',
|
|
23
|
+
async run(args = [], io, context = {}) {
|
|
24
|
+
const [target] = args;
|
|
25
|
+
|
|
26
|
+
if (target && context.commands?.[target]) {
|
|
27
|
+
return context.commands[target].run(['--help'], io, context);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
io.stdout.write(`${topLevelHelp()}\n`);
|
|
31
|
+
return 0;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { materializeGlobalInstall } from '../../global/materialize.js';
|
|
2
|
+
|
|
3
|
+
function initHelp() {
|
|
4
|
+
return [
|
|
5
|
+
'Usage: openkit init',
|
|
6
|
+
'',
|
|
7
|
+
'Compatibility alias for manual global setup.',
|
|
8
|
+
'Most users should run `openkit run`.',
|
|
9
|
+
].join('\n');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const initCommand = {
|
|
13
|
+
name: 'init',
|
|
14
|
+
async run(args = [], io) {
|
|
15
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
16
|
+
io.stdout.write(`${initHelp()}\n`);
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const result = materializeGlobalInstall({ env: process.env });
|
|
21
|
+
io.stdout.write('Installed OpenKit globally.\n');
|
|
22
|
+
io.stdout.write(`Kit root: ${result.kitRoot}\n`);
|
|
23
|
+
return 0;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { materializeGlobalInstall } from '../../global/materialize.js';
|
|
2
|
+
|
|
3
|
+
function installGlobalHelp() {
|
|
4
|
+
return [
|
|
5
|
+
'Usage: openkit install-global',
|
|
6
|
+
'',
|
|
7
|
+
'Manually install OpenKit globally into the OpenCode home directory.',
|
|
8
|
+
'Most users should run `openkit run`, which performs first-time setup automatically.',
|
|
9
|
+
].join('\n');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const installGlobalCommand = {
|
|
13
|
+
name: 'install-global',
|
|
14
|
+
async run(args = [], io) {
|
|
15
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
16
|
+
io.stdout.write(`${installGlobalHelp()}\n`);
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const result = materializeGlobalInstall({ env: process.env });
|
|
21
|
+
io.stdout.write(`Installed OpenKit globally.\n`);
|
|
22
|
+
io.stdout.write(`Kit root: ${result.kitRoot}\n`);
|
|
23
|
+
io.stdout.write(`Profile root: ${result.profilesRoot}\n`);
|
|
24
|
+
return 0;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { materializeGlobalInstall } from '../../global/materialize.js';
|
|
2
|
+
|
|
3
|
+
function installHelp() {
|
|
4
|
+
return [
|
|
5
|
+
'Usage: openkit install',
|
|
6
|
+
'',
|
|
7
|
+
'Compatibility alias for manual global setup.',
|
|
8
|
+
'Most users should run `openkit run`.',
|
|
9
|
+
].join('\n');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const installCommand = {
|
|
13
|
+
name: 'install',
|
|
14
|
+
async run(args = [], io) {
|
|
15
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
16
|
+
io.stdout.write(`${installHelp()}\n`);
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const result = materializeGlobalInstall({ env: process.env });
|
|
21
|
+
io.stdout.write('Installed OpenKit globally.\n');
|
|
22
|
+
io.stdout.write(`Kit root: ${result.kitRoot}\n`);
|
|
23
|
+
return 0;
|
|
24
|
+
},
|
|
25
|
+
};
|