@minhpnq1807/contextos 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +79 -104
- package/bin/ctx.js +35 -1
- package/package.json +1 -1
- package/plugins/ctx/.codex-plugin/plugin.json +1 -1
- package/plugins/ctx/lib/certification.js +3 -0
- package/plugins/ctx/lib/project-context-generator.js +394 -0
- package/plugins/ctx/lib/setup-wizard.js +5 -2
- package/plugins/ctx/lib/skill-discoverer.js +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.5
|
|
4
|
+
|
|
5
|
+
- **Generated skill frontmatter:** Fixed `ctx doctor --fix` starter `SKILL.md` output so generated project skills include YAML frontmatter with `name` and `description`. This prevents invalid-skill warnings after generated skills are synced through skillshare.
|
|
6
|
+
|
|
7
|
+
## 0.6.4
|
|
8
|
+
|
|
9
|
+
- **Project context generator:** Added `ctx doctor --fix` and `ctx setup --generate-project-context` to explicitly scaffold starter project skills and a primary workflow when a repository has rules but is missing ContextOS-ready skills/workflows. Generation is repo-scoped, does not run from `npm install`, and does not overwrite existing files unless `--force` is passed to `ctx doctor --fix`.
|
|
10
|
+
|
|
3
11
|
## 0.6.3
|
|
4
12
|
|
|
5
13
|
- **Launch benchmark wording:** Clarified that `ctx leaderboard --hallucination` is an offline deterministic benchmark comparing a raw heuristic baseline with ContextOS evidence-based context selection, while live agent results remain pending external CLI environments.
|
package/README.md
CHANGED
|
@@ -1,109 +1,45 @@
|
|
|
1
1
|
# ContextOS
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Same prompt. Same model. Different context.
|
|
4
4
|
|
|
5
|
-
ContextOS
|
|
5
|
+
ContextOS stops coding agents from ignoring repo rules, guessing the wrong path, and reading random files.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@minhpnq1807/contextos)
|
|
8
8
|
[](https://github.com/khovan123/contextOS/actions/workflows/ci.yml)
|
|
9
|
-
[](#contextos-ready)
|
|
10
9
|
[](LICENSE)
|
|
11
10
|
|
|
12
11
|
```text
|
|
13
|
-
|
|
14
|
-
Fix
|
|
12
|
+
Prompt:
|
|
13
|
+
Fix deployment
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
Raw Agent:
|
|
16
|
+
❌ Vercel
|
|
17
|
+
❌ Docker
|
|
18
|
+
❌ Railway
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
ContextOS:
|
|
21
|
+
✅ EAS
|
|
22
|
+
✅ Mobile Deployment
|
|
23
|
+
✅ GitHub Actions
|
|
21
24
|
```
|
|
22
25
|
|
|
23
|
-
ContextOS
|
|
26
|
+
ContextOS reads the repo before the agent starts: project rules, repo evidence, suggested files, skills, workflows, and post-task proof.
|
|
24
27
|
|
|
25
28
|
Published package: [`@minhpnq1807/contextos`](https://www.npmjs.com/package/@minhpnq1807/contextos)
|
|
26
29
|
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-

|
|
30
|
-
|
|
31
|
-
Same prompt. Same model. Different context.
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
ctx skills doctor -- "fix deployed"
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
| Repo evidence | What ContextOS tells the agent |
|
|
38
|
-
| --- | --- |
|
|
39
|
-
| `eas.json`, `expo`, `react-native` | `eas`, `mobile-deployment`, `github-actions-ci-cd` |
|
|
40
|
-
| `vercel.json`, `next`, GitHub workflow | `vercel-deployment`, `github-actions-ci-cd`, `env-secret-management` |
|
|
41
|
-
| ContextOS repo with no app deploy evidence | no deployment skill selected |
|
|
42
|
-
|
|
43
|
-
More 10-second demos:
|
|
44
|
-
|
|
45
|
-
| Demo | GIF |
|
|
46
|
-
| --- | --- |
|
|
47
|
-
| AGENTS.md Lost In The Middle | [docs/demo/agents-lost-middle.gif](docs/demo/agents-lost-middle.gif) |
|
|
48
|
-
| ContextOS Ready Gold | [docs/demo/contextos-ready.gif](docs/demo/contextos-ready.gif) |
|
|
49
|
-
|
|
50
|
-
Regenerate the GIFs from real local `ctx` command output:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
npm run demo:capture
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Wrong Path Benchmark
|
|
30
|
+
## Hallucination Leaderboard
|
|
57
31
|
|
|
58
|
-
|
|
32
|
+
Offline deterministic benchmark:
|
|
59
33
|
|
|
60
|
-
|
|
61
|
-
Prompt: Fix deployment
|
|
62
|
-
Raw agent guess: Vercel, Docker, Railway
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
ContextOS checks the repo first:
|
|
66
|
-
|
|
67
|
-
```text
|
|
68
|
-
Detected evidence:
|
|
69
|
-
- eas.json
|
|
70
|
-
- expo dependency
|
|
71
|
-
- GitHub workflow
|
|
72
|
-
|
|
73
|
-
Selected skills:
|
|
74
|
-
- eas
|
|
75
|
-
- mobile-deployment
|
|
76
|
-
- github-actions-ci-cd
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
That is the core launch demo: same prompt, same model, different repo, correct next step.
|
|
80
|
-
|
|
81
|
-
Internal fixture benchmark:
|
|
82
|
-
|
|
83
|
-
| Metric | Result |
|
|
34
|
+
| System | Correct context choice |
|
|
84
35
|
| --- | ---: |
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
| Top-3 Recall | 94.2% |
|
|
88
|
-
| False Positive Rate | 0.0% |
|
|
89
|
-
| Confidence Calibration | 100.0% |
|
|
90
|
-
| Negative Gate Accuracy | 100.0% |
|
|
91
|
-
|
|
92
|
-
This is an internal fixture benchmark, not an external real-world benchmark. It is designed to prove that ContextOS changes its suggestions from repo evidence across controlled Expo/EAS, Next/Vercel, Docker, Railway/Render, Firebase, auth, database, testing, mobile, and adversarial negative cases.
|
|
93
|
-
|
|
94
|
-
Offline hallucination leaderboard:
|
|
36
|
+
| Raw heuristic baseline | 10.0% |
|
|
37
|
+
| ContextOS evidence benchmark | 80.0% |
|
|
95
38
|
|
|
96
39
|
```bash
|
|
97
40
|
ctx leaderboard --hallucination
|
|
98
41
|
```
|
|
99
42
|
|
|
100
|
-
Current deterministic result across 20 fixture tasks and 12 repo contexts:
|
|
101
|
-
|
|
102
|
-
| System | Correct context choice |
|
|
103
|
-
| --- | ---: |
|
|
104
|
-
| Raw heuristic baseline | 10.0% |
|
|
105
|
-
| ContextOS evidence benchmark | 80.0% |
|
|
106
|
-
|
|
107
43
|
This means ContextOS improves deterministic context routing from 10% to 80% on the offline hallucination task set. It does not claim ContextOS beats Codex, Gemini, Claude Code, or Cursor in live runs.
|
|
108
44
|
|
|
109
45
|
Live agent benchmark support exists, but results are pending an external environment with working CLI auth/session access:
|
|
@@ -122,7 +58,36 @@ Live benchmark tracking:
|
|
|
122
58
|
- [Run Gemini CLI live benchmark](https://github.com/khovan123/contextOS/issues/4)
|
|
123
59
|
- [Run Cursor live benchmark](https://github.com/khovan123/contextOS/issues/2)
|
|
124
60
|
|
|
125
|
-
|
|
61
|
+
## Why People Star ContextOS
|
|
62
|
+
|
|
63
|
+
- Agents ignore `AGENTS.md`.
|
|
64
|
+
- Agents choose the wrong deployment path.
|
|
65
|
+
- Agents grep random files before understanding the repo.
|
|
66
|
+
- ContextOS fixes all three before coding starts.
|
|
67
|
+
|
|
68
|
+
## Demo
|
|
69
|
+
|
|
70
|
+

|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
ctx skills doctor -- "fix deployed"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| Repo evidence | What ContextOS tells the agent |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `eas.json`, `expo`, `react-native` | `eas`, `mobile-deployment`, `github-actions-ci-cd` |
|
|
79
|
+
| `vercel.json`, `next`, GitHub workflow | `vercel-deployment`, `github-actions-ci-cd`, `env-secret-management` |
|
|
80
|
+
| ContextOS repo with no app deploy evidence | no deployment skill selected |
|
|
81
|
+
|
|
82
|
+
More 10-second demo:
|
|
83
|
+
|
|
84
|
+
| Demo | GIF |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| AGENTS.md Lost In The Middle | [docs/demo/agents-lost-middle.gif](docs/demo/agents-lost-middle.gif) |
|
|
87
|
+
|
|
88
|
+
## What The Agent Sees
|
|
89
|
+
|
|
90
|
+
ContextOS injects a compact brief before the agent works:
|
|
126
91
|
|
|
127
92
|
```text
|
|
128
93
|
## Critical ContextOS rules
|
|
@@ -148,6 +113,27 @@ Rule outcomes: 8 followed, 0 ignored, 0 unknown
|
|
|
148
113
|
Runtime evidence: project graph was used before file search
|
|
149
114
|
```
|
|
150
115
|
|
|
116
|
+
Regenerate the GIFs from real local `ctx` command output:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm run demo:capture
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Internal Benchmark
|
|
123
|
+
|
|
124
|
+
Skill selection fixture benchmark:
|
|
125
|
+
|
|
126
|
+
| Metric | Result |
|
|
127
|
+
| --- | ---: |
|
|
128
|
+
| Cases | 52 |
|
|
129
|
+
| Top-1 Accuracy | 94.2% |
|
|
130
|
+
| Top-3 Recall | 94.2% |
|
|
131
|
+
| False Positive Rate | 0.0% |
|
|
132
|
+
| Confidence Calibration | 100.0% |
|
|
133
|
+
| Negative Gate Accuracy | 100.0% |
|
|
134
|
+
|
|
135
|
+
This is an internal fixture benchmark, not an external real-world benchmark. It is designed to prove that ContextOS changes its suggestions from repo evidence across controlled Expo/EAS, Next/Vercel, Docker, Railway/Render, Firebase, auth, database, testing, mobile, and adversarial negative cases.
|
|
136
|
+
|
|
151
137
|
## Quick Install
|
|
152
138
|
|
|
153
139
|
Install in 30 seconds:
|
|
@@ -164,6 +150,13 @@ Scriptable setup:
|
|
|
164
150
|
```bash
|
|
165
151
|
ctx setup --yes
|
|
166
152
|
ctx setup --yes --agents codex,claude,agy
|
|
153
|
+
ctx setup --yes --generate-project-context
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
If `ctx doctor` reports missing project skills/workflows, generate starter project context explicitly:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
ctx doctor --fix
|
|
167
160
|
```
|
|
168
161
|
|
|
169
162
|
No global install:
|
|
@@ -188,27 +181,6 @@ ctx install agy
|
|
|
188
181
|
|
|
189
182
|
Restart the agent after setup. Then use the agent normally.
|
|
190
183
|
|
|
191
|
-
## Why ContextOS Exists
|
|
192
|
-
|
|
193
|
-
Developers put real operating instructions in `AGENTS.md`: use this graph tool before reading files, run these tests, follow this architecture boundary, avoid this migration path.
|
|
194
|
-
|
|
195
|
-
The problem is not that agents cannot read `AGENTS.md`. The problem is that large context windows bury the important rule in the middle, where attention is weak.
|
|
196
|
-
|
|
197
|
-
The same thing happens with project structure:
|
|
198
|
-
|
|
199
|
-
- A deployment prompt says "fix deploy", and the agent guesses Vercel in an Expo repo.
|
|
200
|
-
- A backend error mentions Fastify, and the agent loads frontend skills.
|
|
201
|
-
- A feature request names one route, and the agent starts with broad grep instead of the files that matter.
|
|
202
|
-
|
|
203
|
-
ContextOS fixes those three failures before the agent starts work.
|
|
204
|
-
|
|
205
|
-
The next visible demo is not another feature. It is showing the pain in a few seconds:
|
|
206
|
-
|
|
207
|
-
```text
|
|
208
|
-
Raw agent: guesses from the prompt.
|
|
209
|
-
ContextOS: checks repo evidence first.
|
|
210
|
-
```
|
|
211
|
-
|
|
212
184
|
## What ContextOS Does
|
|
213
185
|
|
|
214
186
|
| Agent failure | ContextOS behavior |
|
|
@@ -290,6 +262,7 @@ The score checks project `AGENTS.md` rules, project skill packs under `.codex/sk
|
|
|
290
262
|
| `ctx setup` | Recommended first-run install flow. |
|
|
291
263
|
| `ctx debug -- "Recheck authen flow"` | Preview what ContextOS would inject. |
|
|
292
264
|
| `ctx doctor` | Score repository readiness for the `ContextOS Ready` badge. |
|
|
265
|
+
| `ctx doctor --fix` | Generate starter project skills and workflow when the repo is missing them. |
|
|
293
266
|
| `ctx report` | Show the last task's compliance summary. |
|
|
294
267
|
| `ctx evidence` | Show why each rule was marked followed/ignored/unknown. |
|
|
295
268
|
| `ctx stats` | Show workspace-level usage and effectiveness metrics. |
|
|
@@ -611,12 +584,14 @@ This warning comes from a transitive dependency in the local embedding/WASM stac
|
|
|
611
584
|
| `ctx install --copy` | Copies only the plugin payload to `$CODEX_HOME/plugins/ctx`. | Legacy local development or manual plugin experiments. | Does not sync the active marketplace, rebuild indexes, register MCP, or install global hooks. Prefer `ctx refresh` for active local updates. |
|
|
612
585
|
| `ctx setup` | Runs the first-run setup wizard. | You want the recommended onboarding flow after `npm install -g @minhpnq1807/contextos`. | Installs selected agents, optionally syncs Ruler rules/MCP and skillshare skills, asks which prompt sections to show, then prints next steps. |
|
|
613
586
|
| `ctx setup --yes` | Runs setup with defaults non-interactively. | You want scriptable Codex setup. | Uses `codex`, enables injection, syncs rules, syncs skills, skips interactive community-skill installation when no TTY is available, and passes `--yes` to dependency setup prompts. Use `--agents codex,claude,agy` for multi-agent setup. |
|
|
587
|
+
| `ctx setup --generate-project-context` | Generates starter project skills and workflow during setup. | Your repo has rules but `ctx doctor` reports missing skills/workflows. | Creates `.codex/skills/<detected-skill>/SKILL.md`, matching `skill.yaml`, and `.codex/workflows/primary.md` without overwriting existing files. |
|
|
614
588
|
| `ctx setup --agents <list>` | Runs setup for selected agents. | You want only part of the default set. | Accepts comma-separated `codex`, `claude`, `agy`, or `antigravity`. |
|
|
615
589
|
| `ctx setup --no-rules` | Skips Ruler sync during setup. | You only want hooks/MCP install and maybe skill sync. | Does not run `ctx sync --rules`. |
|
|
616
590
|
| `ctx setup --no-skills` | Skips skillshare sync during setup. | You do not want shared skills configured. | Does not run `ctx sync --skills`. |
|
|
617
591
|
| `ctx setup --quiet` | Runs setup in measurement-only mode. | You want reports/stats without visible injected prompt context. | Installs hooks with prompt context injection disabled. |
|
|
618
592
|
| `ctx debug -- "task"` | Runs the scheduler locally for a fake prompt. | You want to see which AGENTS.md rules and files ContextOS would inject before using Codex. | Prints rule scores, scoring reasons, suggested files, and final `additionalContext`. |
|
|
619
593
|
| `ctx doctor` | Scores repository ContextOS readiness. | You want to add or verify a `ContextOS Ready` badge. | Prints Rules, Skills, Workflows, Overall tier, evidence, and next recommendations. |
|
|
594
|
+
| `ctx doctor --fix` | Generates starter ContextOS project context. | `ctx doctor` says skills/workflows are missing and you want explicit local scaffolding. | Detects package/config evidence, creates up to three starter project skills plus `.codex/workflows/primary.md`, then prints the updated readiness score. |
|
|
620
595
|
| `ctx report` | Shows the last Stop-hook compliance report for the current workspace. | An agent task has finished and you want the summary again. | Prints sectioned tables for summary, rule outcomes, suggested files, and runtime telemetry from `~/.ctx/contextos/workspaces/<workspace-id>/last-report.json`. |
|
|
621
596
|
| `ctx evidence` | Shows detailed evidence behind the last report for the current workspace. | You want to inspect why a rule was marked `followed`, `ignored`, `unknown`, or `unmeasurable`. | Prints a compact evidence table plus per-rule detail tables. |
|
|
622
597
|
| `ctx stats` | Shows aggregate runtime metrics for the current workspace. | You want to know whether ContextOS is active and useful over time. | Prints sectioned tables for prompt/report counts, injection rate, efficiency, rule outcomes, hook events, last prompt, and last report. |
|
package/bin/ctx.js
CHANGED
|
@@ -44,6 +44,7 @@ import { fetchSkillsForAgents, printSkillRecommendations, getAllLibraries, getIn
|
|
|
44
44
|
import { invalidateCtxMcpSocket } from "../plugins/ctx/lib/ctx-mcp-client.js";
|
|
45
45
|
import { runPrefixedCommand } from "../plugins/ctx/lib/shell-runner.js";
|
|
46
46
|
import { formatContextOSReady, inspectContextOSReady } from "../plugins/ctx/lib/certification.js";
|
|
47
|
+
import { formatProjectContextGeneration, generateProjectContext } from "../plugins/ctx/lib/project-context-generator.js";
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
50
|
* Run a shell command with all output lines prefixed by │
|
|
@@ -189,11 +190,14 @@ Usage:
|
|
|
189
190
|
ctx setup Interactive full setup wizard
|
|
190
191
|
ctx setup --yes Auto-confirm all setup prompts
|
|
191
192
|
ctx setup --agents <names> Pre-select agents to install
|
|
193
|
+
ctx setup --generate-project-context Generate starter project skills/workflow
|
|
192
194
|
ctx setup --no-rules Skip AGENTS.md rule sync
|
|
193
195
|
ctx setup --no-skills Skip skill sync
|
|
194
196
|
ctx setup --quiet Quiet mode (minimal output)
|
|
195
197
|
ctx debug -- "task" Debug a task with ContextOS tracing
|
|
196
198
|
ctx doctor Score repository ContextOS readiness
|
|
199
|
+
ctx doctor --fix Generate starter project skills/workflow
|
|
200
|
+
ctx doctor --fix --force Regenerate starter project context files
|
|
197
201
|
ctx report Show last ContextOS compliance report
|
|
198
202
|
ctx evidence Show evidence from last report
|
|
199
203
|
ctx stats Show workspace statistics
|
|
@@ -880,6 +884,22 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
880
884
|
});
|
|
881
885
|
}
|
|
882
886
|
|
|
887
|
+
if (interactive && !options.generateProjectContext) {
|
|
888
|
+
const readiness = inspectContextOSReady({ cwd });
|
|
889
|
+
if (readiness.skills.score < 50 || readiness.workflows.score < 50) {
|
|
890
|
+
const rl = readline.createInterface({ input, output });
|
|
891
|
+
try {
|
|
892
|
+
options.generateProjectContext = await askSetupYesNo(
|
|
893
|
+
rl,
|
|
894
|
+
"Generate starter project skills and workflow?",
|
|
895
|
+
true
|
|
896
|
+
);
|
|
897
|
+
} finally {
|
|
898
|
+
rl.close();
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
883
903
|
console.log("");
|
|
884
904
|
console.log("◇ Ready to setup:");
|
|
885
905
|
for (const line of setupSummaryLines({
|
|
@@ -892,6 +912,13 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
892
912
|
|
|
893
913
|
if (!options.agents.length) throw new Error("No agents selected. Use --agents codex,claude,antigravity,copilot.");
|
|
894
914
|
|
|
915
|
+
if (options.generateProjectContext) {
|
|
916
|
+
console.log("◇ Generating starter project context...");
|
|
917
|
+
const generated = generateProjectContext({ cwd });
|
|
918
|
+
for (const line of formatProjectContextGeneration(generated).split("\n")) console.log(`│ ${line}`);
|
|
919
|
+
console.log("");
|
|
920
|
+
}
|
|
921
|
+
|
|
895
922
|
for (const agent of options.agents) {
|
|
896
923
|
console.log(`◇ Setting up ${agent}...`);
|
|
897
924
|
await streamSetupOutput(() => install({ agent, copy: false }));
|
|
@@ -1022,7 +1049,14 @@ try {
|
|
|
1022
1049
|
if (!task.trim()) throw new Error('Usage: ctx debug -- "task"');
|
|
1023
1050
|
await debug(task);
|
|
1024
1051
|
} else if (command === "doctor") {
|
|
1025
|
-
|
|
1052
|
+
if (args.includes("--fix")) {
|
|
1053
|
+
const generated = generateProjectContext({ cwd: process.cwd(), force: args.includes("--force") });
|
|
1054
|
+
console.log(formatProjectContextGeneration(generated));
|
|
1055
|
+
console.log("");
|
|
1056
|
+
console.log(formatContextOSReady(inspectContextOSReady({ cwd: process.cwd() })));
|
|
1057
|
+
} else {
|
|
1058
|
+
console.log(formatContextOSReady(inspectContextOSReady({ cwd: process.cwd() })));
|
|
1059
|
+
}
|
|
1026
1060
|
} else if (command === "refresh") {
|
|
1027
1061
|
await refresh();
|
|
1028
1062
|
} else if (command === "autowarm") {
|
package/package.json
CHANGED
|
@@ -68,6 +68,9 @@ export function formatContextOSReady(result) {
|
|
|
68
68
|
if (next.length) {
|
|
69
69
|
lines.push("", "Next:");
|
|
70
70
|
for (const item of [...new Set(next)].slice(0, 5)) lines.push(`- ${item}`);
|
|
71
|
+
if (result.skills.score < 50 || result.workflows.score < 50) {
|
|
72
|
+
lines.push("- Run `ctx doctor --fix` to generate starter project skills and workflow.");
|
|
73
|
+
}
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
return lines.join("\n");
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { clearSkillScanCache } from "./skill-discoverer.js";
|
|
5
|
+
|
|
6
|
+
const STARTER_SKILL_LIMIT = 3;
|
|
7
|
+
|
|
8
|
+
export function generateProjectContext({ cwd = process.cwd(), force = false } = {}) {
|
|
9
|
+
const root = findProjectRoot(cwd);
|
|
10
|
+
const profile = detectProjectProfile(root);
|
|
11
|
+
const skills = selectStarterSkills(profile).slice(0, STARTER_SKILL_LIMIT);
|
|
12
|
+
const created = [];
|
|
13
|
+
const skipped = [];
|
|
14
|
+
|
|
15
|
+
for (const skill of skills) {
|
|
16
|
+
const dir = path.join(root, ".codex", "skills", skill.id);
|
|
17
|
+
const skillPath = path.join(dir, "SKILL.md");
|
|
18
|
+
const yamlPath = path.join(dir, "skill.yaml");
|
|
19
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
20
|
+
writeFile({ filePath: skillPath, content: renderSkillMarkdown(skill), force, created, skipped });
|
|
21
|
+
writeFile({ filePath: yamlPath, content: renderSkillYaml(skill), force, created, skipped });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const workflowPath = path.join(root, ".codex", "workflows", "primary.md");
|
|
25
|
+
fs.mkdirSync(path.dirname(workflowPath), { recursive: true });
|
|
26
|
+
writeFile({
|
|
27
|
+
filePath: workflowPath,
|
|
28
|
+
content: renderPrimaryWorkflow(profile, skills),
|
|
29
|
+
force,
|
|
30
|
+
created,
|
|
31
|
+
skipped
|
|
32
|
+
});
|
|
33
|
+
clearSkillScanCache();
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
root,
|
|
37
|
+
profile,
|
|
38
|
+
skills: skills.map((skill) => skill.id),
|
|
39
|
+
created,
|
|
40
|
+
skipped
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function formatProjectContextGeneration(result) {
|
|
45
|
+
const lines = [
|
|
46
|
+
"Project context generated",
|
|
47
|
+
"",
|
|
48
|
+
`Root: ${result.root}`,
|
|
49
|
+
`Detected: ${result.profile.summary}`,
|
|
50
|
+
`Skills: ${result.skills.join(", ") || "(none)"}`,
|
|
51
|
+
"",
|
|
52
|
+
"Created:"
|
|
53
|
+
];
|
|
54
|
+
if (result.created.length) {
|
|
55
|
+
for (const filePath of result.created) lines.push(`- ${path.relative(result.root, filePath)}`);
|
|
56
|
+
} else {
|
|
57
|
+
lines.push("- none");
|
|
58
|
+
}
|
|
59
|
+
if (result.skipped.length) {
|
|
60
|
+
lines.push("", "Skipped existing files:");
|
|
61
|
+
for (const filePath of result.skipped) lines.push(`- ${path.relative(result.root, filePath)}`);
|
|
62
|
+
}
|
|
63
|
+
lines.push("", "Next:");
|
|
64
|
+
lines.push("- Review generated skills/workflow and edit project-specific wording.");
|
|
65
|
+
lines.push("- Run: ctx doctor");
|
|
66
|
+
lines.push("- Run: ctx debug -- \"your task\"");
|
|
67
|
+
return lines.join("\n");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function detectProjectProfile(root) {
|
|
71
|
+
const packageFiles = findPackageJsonFiles(root);
|
|
72
|
+
const packages = packageFiles.map((filePath) => ({
|
|
73
|
+
filePath,
|
|
74
|
+
json: safeJson(filePath)
|
|
75
|
+
})).filter((item) => item.json);
|
|
76
|
+
const dependencies = new Set();
|
|
77
|
+
const scripts = new Set();
|
|
78
|
+
for (const item of packages) {
|
|
79
|
+
for (const section of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
80
|
+
for (const name of Object.keys(item.json[section] || {})) dependencies.add(name);
|
|
81
|
+
}
|
|
82
|
+
for (const name of Object.keys(item.json.scripts || {})) scripts.add(name);
|
|
83
|
+
}
|
|
84
|
+
const files = new Set([
|
|
85
|
+
...findExisting(root, [
|
|
86
|
+
"eas.json",
|
|
87
|
+
"app.json",
|
|
88
|
+
"app.config.js",
|
|
89
|
+
"app.config.ts",
|
|
90
|
+
"vercel.json",
|
|
91
|
+
"Dockerfile",
|
|
92
|
+
"docker-compose.yml",
|
|
93
|
+
"compose.yml",
|
|
94
|
+
"railway.json",
|
|
95
|
+
"render.yaml",
|
|
96
|
+
"firebase.json",
|
|
97
|
+
"prisma/schema.prisma",
|
|
98
|
+
"jest.config.js",
|
|
99
|
+
"jest.config.ts",
|
|
100
|
+
"playwright.config.ts",
|
|
101
|
+
".github/workflows"
|
|
102
|
+
])
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
const has = (name) => dependencies.has(name);
|
|
106
|
+
const hasFile = (name) => files.has(name);
|
|
107
|
+
const platforms = [];
|
|
108
|
+
if (has("expo") || has("react-native") || hasFile("eas.json")) platforms.push("expo-mobile");
|
|
109
|
+
if (has("next") || hasFile("vercel.json")) platforms.push("next-web");
|
|
110
|
+
if (has("@nestjs/core")) platforms.push("nestjs-backend");
|
|
111
|
+
if (has("express")) platforms.push("express-backend");
|
|
112
|
+
if (has("prisma") || has("@prisma/client") || hasFile("prisma/schema.prisma")) platforms.push("prisma");
|
|
113
|
+
if (has("redis") || has("ioredis")) platforms.push("redis");
|
|
114
|
+
if (hasFile("Dockerfile") || hasFile("docker-compose.yml")) platforms.push("docker");
|
|
115
|
+
if (hasFile(".github/workflows")) platforms.push("github-actions");
|
|
116
|
+
if (has("jest") || has("vitest") || hasFile("jest.config.js") || hasFile("playwright.config.ts")) platforms.push("testing");
|
|
117
|
+
|
|
118
|
+
const summary = platforms.length
|
|
119
|
+
? platforms.join(", ")
|
|
120
|
+
: packages.length
|
|
121
|
+
? `${packages.length} package.json file(s)`
|
|
122
|
+
: "generic repository";
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
root,
|
|
126
|
+
packageFiles,
|
|
127
|
+
dependencies,
|
|
128
|
+
scripts,
|
|
129
|
+
files,
|
|
130
|
+
platforms,
|
|
131
|
+
summary
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function selectStarterSkills(profile) {
|
|
136
|
+
const skills = [];
|
|
137
|
+
const hasPlatform = (platform) => profile.platforms.includes(platform);
|
|
138
|
+
if (hasPlatform("expo-mobile")) {
|
|
139
|
+
skills.push(skill({
|
|
140
|
+
id: "mobile-deployment",
|
|
141
|
+
name: "Mobile Deployment",
|
|
142
|
+
description: "Use for Expo, React Native, EAS build, QR/dev-client, Android/iOS preview, and mobile release tasks.",
|
|
143
|
+
prompts: ["expo", "react native", "eas", "mobile", "qr", "android", "ios", "preview", "production", "deploy"],
|
|
144
|
+
files: ["eas.json", "app.json", "app.config.ts", ".github/workflows/*"],
|
|
145
|
+
dependencies: ["expo", "react-native", "eas-cli"],
|
|
146
|
+
negatives: ["vercel", "serverless web deployment"]
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
if (hasPlatform("next-web")) {
|
|
150
|
+
skills.push(skill({
|
|
151
|
+
id: "nextjs-web",
|
|
152
|
+
name: "Next.js Web",
|
|
153
|
+
description: "Use for Next.js routes, App Router, server/client component boundaries, Vercel deploys, and web UI tasks.",
|
|
154
|
+
prompts: ["next", "nextjs", "app router", "route", "page", "component", "vercel", "webapp"],
|
|
155
|
+
files: ["app/**", "pages/**", "next.config.*", "vercel.json"],
|
|
156
|
+
dependencies: ["next", "react"],
|
|
157
|
+
negatives: ["expo", "eas", "android", "ios"]
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
if (hasPlatform("nestjs-backend") || hasPlatform("express-backend")) {
|
|
161
|
+
skills.push(skill({
|
|
162
|
+
id: "backend-api",
|
|
163
|
+
name: "Backend API",
|
|
164
|
+
description: "Use for backend services, API endpoints, validation, auth, controllers/routes, and service-layer changes.",
|
|
165
|
+
prompts: ["api", "backend", "service", "controller", "route", "auth", "validation", "fastify", "express", "nestjs"],
|
|
166
|
+
files: ["services/**", "src/**", "apps/**", "libs/**"],
|
|
167
|
+
dependencies: ["@nestjs/core", "express", "fastify", "zod", "class-validator"],
|
|
168
|
+
negatives: ["pure css", "static copy"]
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
if (hasPlatform("prisma")) {
|
|
172
|
+
skills.push(skill({
|
|
173
|
+
id: "database-prisma",
|
|
174
|
+
name: "Database Prisma",
|
|
175
|
+
description: "Use for Prisma schema, migrations, query performance, repositories, and database-backed tests.",
|
|
176
|
+
prompts: ["prisma", "database", "migration", "query", "schema", "seed", "transaction"],
|
|
177
|
+
files: ["prisma/schema.prisma", "prisma/**", "src/**/repository*", "services/**/repository*"],
|
|
178
|
+
dependencies: ["prisma", "@prisma/client"],
|
|
179
|
+
negatives: ["frontend-only", "css-only"]
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
if (hasPlatform("redis")) {
|
|
183
|
+
skills.push(skill({
|
|
184
|
+
id: "redis-cache",
|
|
185
|
+
name: "Redis Cache",
|
|
186
|
+
description: "Use for cache, queues, sessions, rate limits, Redis clients, and invalidation behavior.",
|
|
187
|
+
prompts: ["redis", "cache", "queue", "session", "rate limit", "invalidation"],
|
|
188
|
+
files: ["src/**/cache*", "services/**/cache*", "libs/**/cache*"],
|
|
189
|
+
dependencies: ["redis", "ioredis", "bullmq"],
|
|
190
|
+
negatives: ["static page"]
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
if (hasPlatform("docker") || hasPlatform("github-actions")) {
|
|
194
|
+
skills.push(skill({
|
|
195
|
+
id: "ci-deployment",
|
|
196
|
+
name: "CI Deployment",
|
|
197
|
+
description: "Use for Docker, GitHub Actions, build logs, deploy failures, environment variables, and release pipelines.",
|
|
198
|
+
prompts: ["ci", "github actions", "docker", "deploy", "build failed", "pipeline", "environment", "secret"],
|
|
199
|
+
files: [".github/workflows/*", "Dockerfile", "docker-compose.yml", "railway.json", "render.yaml"],
|
|
200
|
+
dependencies: [],
|
|
201
|
+
negatives: ["local ui styling only"]
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
if (hasPlatform("testing")) {
|
|
205
|
+
skills.push(skill({
|
|
206
|
+
id: "project-testing",
|
|
207
|
+
name: "Project Testing",
|
|
208
|
+
description: "Use for unit, integration, e2e, Jest, Vitest, Playwright, and focused verification tasks.",
|
|
209
|
+
prompts: ["test", "jest", "vitest", "playwright", "e2e", "coverage", "failing test"],
|
|
210
|
+
files: ["test/**", "__tests__/**", "*.spec.*", "*.test.*", "playwright.config.ts"],
|
|
211
|
+
dependencies: ["jest", "vitest", "@playwright/test"],
|
|
212
|
+
negatives: ["docs-only"]
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
while (skills.length < STARTER_SKILL_LIMIT) {
|
|
216
|
+
const fallback = [
|
|
217
|
+
skill({
|
|
218
|
+
id: "project-implementation",
|
|
219
|
+
name: "Project Implementation",
|
|
220
|
+
description: "Use for normal feature work, bug fixes, and scoped implementation tasks in this repository.",
|
|
221
|
+
prompts: ["implement", "fix", "add", "update", "refactor", "bug"],
|
|
222
|
+
files: ["src/**", "apps/**", "services/**", "libs/**"],
|
|
223
|
+
dependencies: [],
|
|
224
|
+
negatives: ["release notes only"]
|
|
225
|
+
}),
|
|
226
|
+
skill({
|
|
227
|
+
id: "project-debugging",
|
|
228
|
+
name: "Project Debugging",
|
|
229
|
+
description: "Use for runtime errors, failed commands, logs, CI failures, and root-cause analysis.",
|
|
230
|
+
prompts: ["error", "failed", "timeout", "debug", "logs", "cannot", "fix"],
|
|
231
|
+
files: ["package.json", ".github/workflows/*", "src/**", "services/**"],
|
|
232
|
+
dependencies: [],
|
|
233
|
+
negatives: ["new feature with no failure"]
|
|
234
|
+
}),
|
|
235
|
+
skill({
|
|
236
|
+
id: "project-documentation",
|
|
237
|
+
name: "Project Documentation",
|
|
238
|
+
description: "Use for README, changelog, architecture notes, specs, and project documentation updates.",
|
|
239
|
+
prompts: ["readme", "docs", "documentation", "changelog", "spec", "guide"],
|
|
240
|
+
files: ["README.md", "docs/**", "CHANGELOG.md", "AGENTS.md"],
|
|
241
|
+
dependencies: [],
|
|
242
|
+
negatives: ["runtime bug"]
|
|
243
|
+
})
|
|
244
|
+
].find((item) => !skills.some((existing) => existing.id === item.id));
|
|
245
|
+
if (!fallback) break;
|
|
246
|
+
skills.push(fallback);
|
|
247
|
+
}
|
|
248
|
+
return dedupeById(skills);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function skill({ id, name, description, prompts, files, dependencies, negatives }) {
|
|
252
|
+
return { id, name, description, prompts, files, dependencies, negatives };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function renderSkillMarkdown(skill) {
|
|
256
|
+
return [
|
|
257
|
+
"---",
|
|
258
|
+
`name: ${skill.id}`,
|
|
259
|
+
`description: ${skill.description}`,
|
|
260
|
+
"---",
|
|
261
|
+
"",
|
|
262
|
+
`# ${skill.name}`,
|
|
263
|
+
"",
|
|
264
|
+
skill.description,
|
|
265
|
+
"",
|
|
266
|
+
"Use this skill when the prompt and project evidence match the metadata in `skill.yaml`.",
|
|
267
|
+
"",
|
|
268
|
+
"Before editing:",
|
|
269
|
+
"",
|
|
270
|
+
"1. Read the relevant project rules.",
|
|
271
|
+
"2. Inspect the suggested files and nearby tests.",
|
|
272
|
+
"3. Keep the change scoped to the task.",
|
|
273
|
+
"4. Run the focused verification command before final response.",
|
|
274
|
+
""
|
|
275
|
+
].join("\n");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function renderSkillYaml(skill) {
|
|
279
|
+
return [
|
|
280
|
+
`id: ${skill.id}`,
|
|
281
|
+
`name: ${skill.name}`,
|
|
282
|
+
`description: ${skill.description}`,
|
|
283
|
+
"positive_triggers:",
|
|
284
|
+
" prompts:",
|
|
285
|
+
...skill.prompts.map((item) => ` - ${quoteYaml(item)}`),
|
|
286
|
+
" files:",
|
|
287
|
+
...skill.files.map((item) => ` - ${quoteYaml(item)}`),
|
|
288
|
+
" dependencies:",
|
|
289
|
+
...(skill.dependencies.length ? skill.dependencies.map((item) => ` - ${quoteYaml(item)}`) : [" - package.json"]),
|
|
290
|
+
"evidence:",
|
|
291
|
+
" files:",
|
|
292
|
+
...skill.files.slice(0, 4).map((item) => ` - ${quoteYaml(item)}`),
|
|
293
|
+
" dependencies:",
|
|
294
|
+
...(skill.dependencies.length ? skill.dependencies.map((item) => ` - ${quoteYaml(item)}`) : [" - package.json"]),
|
|
295
|
+
"negative_triggers:",
|
|
296
|
+
" prompts:",
|
|
297
|
+
...skill.negatives.map((item) => ` - ${quoteYaml(item)}`),
|
|
298
|
+
"workflow:",
|
|
299
|
+
" - Inspect repo evidence before choosing an implementation path.",
|
|
300
|
+
" - Read the suggested files and nearest tests.",
|
|
301
|
+
" - Implement the smallest scoped change.",
|
|
302
|
+
" - Run focused verification and summarize evidence.",
|
|
303
|
+
""
|
|
304
|
+
].join("\n");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function renderPrimaryWorkflow(profile, skills) {
|
|
308
|
+
return [
|
|
309
|
+
"# Primary Workflow",
|
|
310
|
+
"",
|
|
311
|
+
`Use this workflow for common tasks in this repository. Detected project context: ${profile.summary}.`,
|
|
312
|
+
"",
|
|
313
|
+
"planner -> tester -> code-reviewer -> docs-manager",
|
|
314
|
+
"",
|
|
315
|
+
"1. Read the task and relevant AGENTS.md rules.",
|
|
316
|
+
"2. Check ContextOS suggested files and skills.",
|
|
317
|
+
"3. Inspect project config before choosing a deployment/framework path.",
|
|
318
|
+
"4. Implement the smallest scoped change.",
|
|
319
|
+
"5. Run focused tests or the closest available verification.",
|
|
320
|
+
"6. Summarize files changed, verification, and any remaining risk.",
|
|
321
|
+
"",
|
|
322
|
+
"Starter skills:",
|
|
323
|
+
...skills.map((skill) => `- ${skill.id}: ${skill.description}`),
|
|
324
|
+
""
|
|
325
|
+
].join("\n");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function writeFile({ filePath, content, force, created, skipped }) {
|
|
329
|
+
if (fs.existsSync(filePath) && !force) {
|
|
330
|
+
skipped.push(filePath);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
334
|
+
created.push(filePath);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function findPackageJsonFiles(root) {
|
|
338
|
+
const files = [];
|
|
339
|
+
walk(root, files, (filePath) => path.basename(filePath) === "package.json", 0);
|
|
340
|
+
return files.slice(0, 20);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function findExisting(root, relativePaths) {
|
|
344
|
+
return relativePaths.filter((relativePath) => fs.existsSync(path.join(root, relativePath)));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function walk(directory, files, predicate, depth) {
|
|
348
|
+
if (depth > 4) return;
|
|
349
|
+
let entries = [];
|
|
350
|
+
try {
|
|
351
|
+
entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
352
|
+
} catch {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
for (const entry of entries) {
|
|
356
|
+
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".ctx") continue;
|
|
357
|
+
const filePath = path.join(directory, entry.name);
|
|
358
|
+
if (entry.isDirectory()) walk(filePath, files, predicate, depth + 1);
|
|
359
|
+
else if (entry.isFile() && predicate(filePath)) files.push(filePath);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function safeJson(filePath) {
|
|
364
|
+
try {
|
|
365
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
366
|
+
} catch {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function findProjectRoot(cwd) {
|
|
372
|
+
let current = path.resolve(cwd);
|
|
373
|
+
while (true) {
|
|
374
|
+
if (fs.existsSync(path.join(current, ".git"))) return current;
|
|
375
|
+
const parent = path.dirname(current);
|
|
376
|
+
if (parent === current) return path.resolve(cwd);
|
|
377
|
+
current = parent;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function dedupeById(skills) {
|
|
382
|
+
const seen = new Set();
|
|
383
|
+
const result = [];
|
|
384
|
+
for (const skill of skills) {
|
|
385
|
+
if (seen.has(skill.id)) continue;
|
|
386
|
+
seen.add(skill.id);
|
|
387
|
+
result.push(skill);
|
|
388
|
+
}
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function quoteYaml(value) {
|
|
393
|
+
return JSON.stringify(String(value));
|
|
394
|
+
}
|
|
@@ -19,7 +19,8 @@ export function parseSetupArgs(args = []) {
|
|
|
19
19
|
yes,
|
|
20
20
|
quiet: args.includes("--quiet"),
|
|
21
21
|
syncRules: !args.includes("--no-rules"),
|
|
22
|
-
syncSkills: !args.includes("--no-skills")
|
|
22
|
+
syncSkills: !args.includes("--no-skills"),
|
|
23
|
+
generateProjectContext: args.includes("--generate-project-context")
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -42,6 +43,7 @@ export function setupSummaryLines({
|
|
|
42
43
|
agents = DEFAULT_AGENTS,
|
|
43
44
|
syncRules = true,
|
|
44
45
|
syncSkills = true,
|
|
46
|
+
generateProjectContext = false,
|
|
45
47
|
promptSections = null,
|
|
46
48
|
promptLimits = null
|
|
47
49
|
} = {}) {
|
|
@@ -50,7 +52,8 @@ export function setupSummaryLines({
|
|
|
50
52
|
`Agents: ${agents.join(", ") || "(none)"}`,
|
|
51
53
|
`Prompt context injection: always enabled`,
|
|
52
54
|
`Ruler rule/MCP sync: ${syncRules ? "enabled" : "skipped"}`,
|
|
53
|
-
`skillshare skill sync: ${syncSkills ? "enabled" : "skipped"}
|
|
55
|
+
`skillshare skill sync: ${syncSkills ? "enabled" : "skipped"}`,
|
|
56
|
+
`Project context generation: ${generateProjectContext ? "enabled" : "skipped"}`
|
|
54
57
|
];
|
|
55
58
|
if (promptSections !== null) lines.push(`Prompt sections shown: ${promptSections}`);
|
|
56
59
|
if (promptLimits !== null) lines.push(`Prompt suggest limits: ${promptLimits}`);
|
|
@@ -20,6 +20,10 @@ const DEFAULT_ROUTER_THRESHOLD = 0.35;
|
|
|
20
20
|
|
|
21
21
|
const scanCache = new Map();
|
|
22
22
|
|
|
23
|
+
export function clearSkillScanCache() {
|
|
24
|
+
scanCache.clear();
|
|
25
|
+
}
|
|
26
|
+
|
|
23
27
|
export function skillSearchRoots({ cwd = process.cwd(), home = os.homedir() } = {}) {
|
|
24
28
|
return [
|
|
25
29
|
path.join(cwd, ".codex", "skills"),
|