@minhpnq1807/contextos 0.5.10 → 0.5.12
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 +9 -0
- package/README.md +14 -2
- package/bin/ctx.js +4 -1
- package/package.json +1 -1
- package/plugins/ctx/lib/workflow-discoverer.js +85 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.12
|
|
4
|
+
|
|
5
|
+
- Makes `ctx sync --workflows` synchronize unique workflow files to global Claude, Codex, and Antigravity workflow roots.
|
|
6
|
+
- Adds `ctx sync --workflows --agents ...` and `--dry-run`, with workflow-name dedupe to avoid duplicated workflow suggestions across agents.
|
|
7
|
+
|
|
8
|
+
## 0.5.11
|
|
9
|
+
|
|
10
|
+
- Adds Antigravity workflow discovery roots under `.gemini/workflows`, `.gemini/antigravity/workflows`, and `.gemini/antigravity-cli/workflows`.
|
|
11
|
+
|
|
3
12
|
## 0.5.10
|
|
4
13
|
|
|
5
14
|
- Adds workflow discovery for `.claude/workflows/`, `.codex/workflows/`, `~/.claude/workflows/`, and `~/.codex/workflows/`.
|
package/README.md
CHANGED
|
@@ -216,10 +216,12 @@ After this, `ctx debug -- "task"` and prompt hooks can suggest skills from `~/.c
|
|
|
216
216
|
|
|
217
217
|
## Workflow Discovery
|
|
218
218
|
|
|
219
|
-
ContextOS can also
|
|
219
|
+
ContextOS can also sync Claude/Codex/Antigravity workflow markdown files and suggest the right workflow for the current task:
|
|
220
220
|
|
|
221
221
|
```bash
|
|
222
222
|
ctx sync --workflows
|
|
223
|
+
ctx sync --workflows --agents codex,claude,agy
|
|
224
|
+
ctx sync --workflows --dry-run
|
|
223
225
|
```
|
|
224
226
|
|
|
225
227
|
It scans project workflows first, then global workflows:
|
|
@@ -227,12 +229,20 @@ It scans project workflows first, then global workflows:
|
|
|
227
229
|
```text
|
|
228
230
|
.claude/workflows/
|
|
229
231
|
.codex/workflows/
|
|
232
|
+
.gemini/workflows/
|
|
233
|
+
.gemini/antigravity/workflows/
|
|
234
|
+
.gemini/antigravity-cli/workflows/
|
|
230
235
|
~/.claude/workflows/
|
|
231
236
|
~/.codex/workflows/
|
|
237
|
+
~/.gemini/workflows/
|
|
238
|
+
~/.gemini/antigravity/workflows/
|
|
239
|
+
~/.gemini/antigravity-cli/workflows/
|
|
232
240
|
```
|
|
233
241
|
|
|
234
242
|
Workflow files do not need YAML frontmatter. ContextOS reads the top `#` heading, section headings, and referenced agent names such as `planner`, `tester`, `code-reviewer`, and `docs-manager`, then warms semantic embeddings. Prompt hooks inject a `Suggested workflow for this task` section only when a workflow is relevant enough.
|
|
235
243
|
|
|
244
|
+
`ctx sync --workflows` reads every known project/global workflow root, keeps the first workflow for each filename/name according to root priority, then copies that unique set to the selected global agent roots. This prevents duplicate `primary-workflow` suggestions when the same workflow exists in Claude, Codex, and Antigravity directories.
|
|
245
|
+
|
|
236
246
|
## Modes
|
|
237
247
|
|
|
238
248
|
Injection mode is the default:
|
|
@@ -378,7 +388,9 @@ This warning comes from a transitive dependency in the local embedding/WASM stac
|
|
|
378
388
|
| `ctx sync --skills --agents <list>` | Syncs skills only for selected agents. | You want to target a subset such as `codex,claude` or `codex,claude,agy`. | Runs `skillshare sync --agents <list>` with `agy` normalized to `antigravity`, then refreshes skill embeddings. |
|
|
379
389
|
| `ctx sync --skills --dry-run` | Previews skillshare sync. | You want to inspect behavior before changing skill directories. | Runs `skillshare sync --dry-run` and skips embedding rebuild. |
|
|
380
390
|
| `ctx sync --skills --no-collect` | Skips collecting existing agent skills into skillshare. | You already manage `~/.config/skillshare/skills` and only want to push it out. | Initializes/syncs skillshare without running `skillshare backup` or `skillshare collect --all`. |
|
|
381
|
-
| `ctx sync --workflows` |
|
|
391
|
+
| `ctx sync --workflows` | Syncs and indexes agent workflow markdown files for prompt-time workflow suggestions. | You use `.claude/workflows/`, `.codex/workflows/`, or Antigravity workflow folders and want every agent to see the same deduped workflow set. | Scans project/global workflow folders, dedupes by workflow name, copies unique workflows to selected global agent roots, warms workflow embeddings, and makes `ctx debug`/prompt hooks show relevant workflow hints. |
|
|
392
|
+
| `ctx sync --workflows --agents <list>` | Syncs workflows only for selected agents. | You want a subset such as `codex,claude` or `codex,claude,agy`. | Accepts comma-separated `codex`, `claude`, `agy`, or `antigravity`; `agy` writes the Gemini/Antigravity workflow roots. |
|
|
393
|
+
| `ctx sync --workflows --dry-run` | Previews workflow sync without writing files. | You want to inspect source workflows and target roots first. | Prints planned sync/index output and skips copying target files. |
|
|
382
394
|
| `ctx embeddings warm -- "task"` | Prepares local semantic embedding caches. | First install, CI smoke checks, or after changing AGENTS.md/project files/skills/workflows. | Loads/downloads `Xenova/all-MiniLM-L6-v2` and writes rule, file-path, skill, and workflow vectors to `~/.ctx/contextos/embeddings.db`. |
|
|
383
395
|
| `ctx ruler -- <args>` | Forwards args to the installed `ruler` CLI. | You need native Ruler commands such as `init`, `apply`, or `revert`. | Preserves Ruler stdout/stderr and exit status. |
|
|
384
396
|
| `ctx skillshare -- <args>` | Forwards args to the installed `skillshare` CLI. | You need native skillshare commands such as `status`, `target list`, `doctor`, `push`, or `pull`. | Preserves skillshare stdout/stderr and exit status. |
|
package/bin/ctx.js
CHANGED
|
@@ -63,6 +63,8 @@ Usage:
|
|
|
63
63
|
ctx sync --rules --no-import-codex-mcp
|
|
64
64
|
ctx sync --skills
|
|
65
65
|
ctx sync --workflows
|
|
66
|
+
ctx sync --workflows --agents codex,claude,agy
|
|
67
|
+
ctx sync --workflows --dry-run
|
|
66
68
|
ctx sync --skills --dry-run
|
|
67
69
|
ctx sync --skills --no-collect
|
|
68
70
|
ctx sync --skills --agents codex,claude,antigravity
|
|
@@ -571,7 +573,8 @@ try {
|
|
|
571
573
|
await syncWorkflows({
|
|
572
574
|
cwd: process.cwd(),
|
|
573
575
|
dataDir: contextOSDataDir(),
|
|
574
|
-
allowRemote: !isModelCacheReady(contextOSDataDir())
|
|
576
|
+
allowRemote: !isModelCacheReady(contextOSDataDir()),
|
|
577
|
+
args: args.slice(1)
|
|
575
578
|
});
|
|
576
579
|
} else if (args.includes("--skills")) {
|
|
577
580
|
await syncSkills({
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@ const DEFAULT_LIMIT = 2;
|
|
|
8
8
|
const MIN_WORKFLOW_BYTES = 100;
|
|
9
9
|
const MAX_DESCRIPTION_CHARS = 500;
|
|
10
10
|
const DEFAULT_EMBEDDING_CANDIDATES = 40;
|
|
11
|
+
const DEFAULT_SYNC_AGENTS = ["claude", "codex", "agy"];
|
|
11
12
|
const KNOWN_AGENT_NAMES = new Set([
|
|
12
13
|
"planner",
|
|
13
14
|
"tester",
|
|
@@ -31,14 +32,44 @@ export function workflowSearchRoots({ cwd = process.cwd(), home = os.homedir() }
|
|
|
31
32
|
return [
|
|
32
33
|
path.join(cwd, ".claude", "workflows"),
|
|
33
34
|
path.join(cwd, ".codex", "workflows"),
|
|
35
|
+
path.join(cwd, ".gemini", "workflows"),
|
|
36
|
+
path.join(cwd, ".gemini", "antigravity", "workflows"),
|
|
37
|
+
path.join(cwd, ".gemini", "antigravity-cli", "workflows"),
|
|
34
38
|
path.join(home, ".claude", "workflows"),
|
|
35
|
-
path.join(home, ".codex", "workflows")
|
|
39
|
+
path.join(home, ".codex", "workflows"),
|
|
40
|
+
path.join(home, ".gemini", "workflows"),
|
|
41
|
+
path.join(home, ".gemini", "antigravity", "workflows"),
|
|
42
|
+
path.join(home, ".gemini", "antigravity-cli", "workflows")
|
|
36
43
|
];
|
|
37
44
|
}
|
|
38
45
|
|
|
46
|
+
export function workflowGlobalRoots({ home = os.homedir(), agents = DEFAULT_SYNC_AGENTS } = {}) {
|
|
47
|
+
const normalizedAgents = parseWorkflowAgents(agents);
|
|
48
|
+
const roots = [];
|
|
49
|
+
if (normalizedAgents.includes("claude")) roots.push(path.join(home, ".claude", "workflows"));
|
|
50
|
+
if (normalizedAgents.includes("codex")) roots.push(path.join(home, ".codex", "workflows"));
|
|
51
|
+
if (normalizedAgents.includes("agy")) {
|
|
52
|
+
roots.push(path.join(home, ".gemini", "workflows"));
|
|
53
|
+
roots.push(path.join(home, ".gemini", "antigravity", "workflows"));
|
|
54
|
+
roots.push(path.join(home, ".gemini", "antigravity-cli", "workflows"));
|
|
55
|
+
}
|
|
56
|
+
return roots;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function parseWorkflowAgents(value = DEFAULT_SYNC_AGENTS) {
|
|
60
|
+
const raw = Array.isArray(value) ? value : String(value || "").split(",");
|
|
61
|
+
const agents = raw
|
|
62
|
+
.map((agent) => String(agent || "").trim().toLowerCase())
|
|
63
|
+
.map((agent) => agent === "antigravity" ? "agy" : agent)
|
|
64
|
+
.filter(Boolean);
|
|
65
|
+
const known = agents.filter((agent) => DEFAULT_SYNC_AGENTS.includes(agent));
|
|
66
|
+
return [...new Set(known.length ? known : DEFAULT_SYNC_AGENTS)];
|
|
67
|
+
}
|
|
68
|
+
|
|
39
69
|
export function scanWorkflows({ cwd = process.cwd(), roots = workflowSearchRoots({ cwd }) } = {}) {
|
|
40
70
|
const workflows = [];
|
|
41
71
|
const seen = new Set();
|
|
72
|
+
const seenNames = new Set();
|
|
42
73
|
for (const root of roots) {
|
|
43
74
|
let entries = [];
|
|
44
75
|
try {
|
|
@@ -53,6 +84,8 @@ export function scanWorkflows({ cwd = process.cwd(), roots = workflowSearchRoots
|
|
|
53
84
|
if (seen.has(realPath)) continue;
|
|
54
85
|
seen.add(realPath);
|
|
55
86
|
const workflow = parseWorkflowFile(filePath, { cwd, root });
|
|
87
|
+
if (workflow?.name && seenNames.has(workflow.name)) continue;
|
|
88
|
+
if (workflow?.name) seenNames.add(workflow.name);
|
|
56
89
|
if (workflow) workflows.push(workflow);
|
|
57
90
|
}
|
|
58
91
|
}
|
|
@@ -139,20 +172,67 @@ export async function syncWorkflows({
|
|
|
139
172
|
cwd = process.cwd(),
|
|
140
173
|
dataDir,
|
|
141
174
|
allowRemote = true,
|
|
175
|
+
args = [],
|
|
176
|
+
home = os.homedir(),
|
|
142
177
|
logger = console.log
|
|
143
178
|
} = {}) {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
179
|
+
const options = parseSyncWorkflowArgs(args);
|
|
180
|
+
const agents = parseWorkflowAgents(options.agents);
|
|
181
|
+
const workflows = scanWorkflows({ cwd, roots: workflowSearchRoots({ cwd, home }) });
|
|
182
|
+
const targets = workflowGlobalRoots({ home, agents });
|
|
183
|
+
|
|
184
|
+
logger("ContextOS workflow sync");
|
|
185
|
+
logger(`Agents: ${agents.join(", ")}`);
|
|
186
|
+
logger(`Found unique workflows: ${workflows.length}`);
|
|
147
187
|
if (workflows.length) {
|
|
148
188
|
for (const workflow of workflows) {
|
|
149
189
|
logger(`- ${workflow.relativePath || workflow.path} (${workflow.chain.join(" -> ") || "no chain"})`);
|
|
150
190
|
}
|
|
151
191
|
}
|
|
192
|
+
const syncResult = syncWorkflowFiles({ workflows, targets, dryRun: options.dryRun, logger });
|
|
152
193
|
const result = await warmWorkflowEmbeddings({ cwd, dataDir, allowRemote, workflows });
|
|
194
|
+
logger(`Synced workflows: ${syncResult.copied}${options.dryRun ? " planned" : ""}`);
|
|
195
|
+
logger(`Skipped duplicates: ${syncResult.duplicates}`);
|
|
153
196
|
logger(`Indexed workflows: ${workflows.length}`);
|
|
154
197
|
if (result.cachePath) logger(`Cache: ${result.cachePath}`);
|
|
155
|
-
return { workflows, embeddings: result };
|
|
198
|
+
return { workflows, embeddings: result, sync: syncResult };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function parseSyncWorkflowArgs(args = []) {
|
|
202
|
+
const agentsFlag = args.indexOf("--agents");
|
|
203
|
+
return {
|
|
204
|
+
agents: agentsFlag >= 0 ? args[agentsFlag + 1] : DEFAULT_SYNC_AGENTS,
|
|
205
|
+
dryRun: args.includes("--dry-run")
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function syncWorkflowFiles({ workflows = [], targets = [], dryRun = false, logger = console.log } = {}) {
|
|
210
|
+
let copied = 0;
|
|
211
|
+
let duplicates = 0;
|
|
212
|
+
const seenNames = new Set();
|
|
213
|
+
for (const workflow of workflows) {
|
|
214
|
+
if (seenNames.has(workflow.name)) {
|
|
215
|
+
duplicates += 1;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
seenNames.add(workflow.name);
|
|
219
|
+
for (const targetRoot of targets) {
|
|
220
|
+
const targetPath = path.join(targetRoot, `${workflow.name}.md`);
|
|
221
|
+
const sourceRealPath = safeRealpath(workflow.path) || path.resolve(workflow.path);
|
|
222
|
+
const targetRealPath = safeRealpath(targetPath) || path.resolve(targetPath);
|
|
223
|
+
if (sourceRealPath === targetRealPath) continue;
|
|
224
|
+
if (!dryRun) {
|
|
225
|
+
fs.mkdirSync(targetRoot, { recursive: true });
|
|
226
|
+
fs.copyFileSync(workflow.path, targetPath);
|
|
227
|
+
}
|
|
228
|
+
copied += 1;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (targets.length) {
|
|
232
|
+
logger(`Target roots: ${targets.length}`);
|
|
233
|
+
for (const target of targets) logger(` -> ${target}`);
|
|
234
|
+
}
|
|
235
|
+
return { copied, duplicates, targets: targets.length };
|
|
156
236
|
}
|
|
157
237
|
|
|
158
238
|
function stripFrontmatter(content) {
|