facult 2.2.0 → 2.3.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/README.md +19 -1
- package/bin/fclt.cjs +55 -3
- package/package.json +1 -1
- package/src/ai.ts +13 -2
- package/src/remote.ts +70 -1
package/README.md
CHANGED
|
@@ -694,13 +694,21 @@ fclt snippets sync [--dry-run] [file...]
|
|
|
694
694
|
|
|
695
695
|
### Codex automations
|
|
696
696
|
|
|
697
|
-
`templates init automation` can scaffold
|
|
697
|
+
`templates init automation` can scaffold three Codex automation forms:
|
|
698
698
|
|
|
699
699
|
- `--scope project` (single repo): set `--project-root` (or infer from current working directory)
|
|
700
700
|
- `--scope wide|global` (multiple repos): set `--cwds` explicitly; if omitted, created automation has no `cwds` by default.
|
|
701
701
|
- If you run it interactively without `--scope`, `fclt` prompts for scope and, where possible, known workspaces (git worktrees, configured scan roots, and existing Codex automation paths).
|
|
702
702
|
- Built-in automation templates are opinionated: they reference the global Codex operating model, point at relevant Codex skills, and tell Codex when to use focused subagents for bounded review work.
|
|
703
703
|
|
|
704
|
+
Recommended topology:
|
|
705
|
+
|
|
706
|
+
- Use `learning-review --scope project` for repo-local writeback and evolution. This keeps review state, verification, and follow-up scoped to the repo that actually produced the evidence.
|
|
707
|
+
- Use `evolution-review` on a slower cadence, usually weekly, to triage open proposals and proposal-worthy clusters and suggest the next operator action (`draft`, `review`, `accept`, `reject`, `promote`, or `apply`).
|
|
708
|
+
- Use a separate wide/global automation only for cross-repo or shared-surface review, such as global doctrine, shared skills, or repeated tool/agent patterns across repos.
|
|
709
|
+
- If you do use a wide learning review, keep the `cwds` list intentionally small and related. The prompt is designed to partition by cwd first, not to blur unrelated repos together.
|
|
710
|
+
- A practical default is daily `learning-review` plus weekly `evolution-review`. The first finds and records durable signal; the second keeps proposal review from stalling.
|
|
711
|
+
|
|
704
712
|
Files are written to:
|
|
705
713
|
|
|
706
714
|
- `~/.codex/automations/<name>/automation.toml`
|
|
@@ -725,6 +733,16 @@ fclt templates init automation learning-review \
|
|
|
725
733
|
--status PAUSED
|
|
726
734
|
```
|
|
727
735
|
|
|
736
|
+
Example weekly evolution automation:
|
|
737
|
+
|
|
738
|
+
```bash
|
|
739
|
+
fclt templates init automation evolution-review \
|
|
740
|
+
--scope wide \
|
|
741
|
+
--cwds /path/to/repo-a,/path/to/repo-b \
|
|
742
|
+
--name weekly-evolution-review \
|
|
743
|
+
--status PAUSED
|
|
744
|
+
```
|
|
745
|
+
|
|
728
746
|
Interactive prompt example:
|
|
729
747
|
|
|
730
748
|
```bash
|
package/bin/fclt.cjs
CHANGED
|
@@ -38,15 +38,16 @@ async function main() {
|
|
|
38
38
|
);
|
|
39
39
|
const binaryName = resolved.platform === "windows" ? "fclt.exe" : "fclt";
|
|
40
40
|
const binaryPath = path.join(installDir, binaryName);
|
|
41
|
+
const sourceEntry = path.join(__dirname, "..", "src", "index.ts");
|
|
41
42
|
|
|
42
43
|
if (!(await fileExists(binaryPath))) {
|
|
43
44
|
const tag = `v${version}`;
|
|
44
45
|
const assetName = `${PACKAGE_NAME}-${version}-${resolved.platform}-${resolved.arch}${resolved.ext}`;
|
|
45
46
|
const url = `https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/${tag}/${assetName}`;
|
|
46
|
-
|
|
47
|
-
await fsp.mkdir(installDir, { recursive: true });
|
|
48
47
|
const tmpPath = `${binaryPath}.tmp-${Date.now()}`;
|
|
48
|
+
|
|
49
49
|
try {
|
|
50
|
+
await fsp.mkdir(installDir, { recursive: true });
|
|
50
51
|
await downloadWithRetry(url, tmpPath, {
|
|
51
52
|
attempts: DOWNLOAD_RETRIES,
|
|
52
53
|
delayMs: DOWNLOAD_RETRY_DELAY_MS,
|
|
@@ -57,6 +58,14 @@ async function main() {
|
|
|
57
58
|
await fsp.rename(tmpPath, binaryPath);
|
|
58
59
|
} catch (error) {
|
|
59
60
|
await safeUnlink(tmpPath);
|
|
61
|
+
if (await canUseSourceFallback(sourceEntry)) {
|
|
62
|
+
return runSourceFallback({
|
|
63
|
+
sourceEntry,
|
|
64
|
+
version,
|
|
65
|
+
packageManager: detectPackageManager(),
|
|
66
|
+
reason: error,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
60
69
|
const message =
|
|
61
70
|
error instanceof Error ? error.message : String(error ?? "");
|
|
62
71
|
console.error(
|
|
@@ -75,7 +84,7 @@ async function main() {
|
|
|
75
84
|
}
|
|
76
85
|
|
|
77
86
|
const packageManager = detectPackageManager();
|
|
78
|
-
await
|
|
87
|
+
await bestEffortWriteInstallState({
|
|
79
88
|
method: "npm-binary-cache",
|
|
80
89
|
version,
|
|
81
90
|
binaryPath,
|
|
@@ -100,6 +109,41 @@ async function main() {
|
|
|
100
109
|
process.exit(1);
|
|
101
110
|
}
|
|
102
111
|
|
|
112
|
+
async function canUseSourceFallback(sourceEntry) {
|
|
113
|
+
if (!(await fileExists(sourceEntry))) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const result = spawnSync("bun", ["--version"], {
|
|
117
|
+
stdio: "ignore",
|
|
118
|
+
env: process.env,
|
|
119
|
+
});
|
|
120
|
+
return result.status === 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function runSourceFallback({ sourceEntry, version, packageManager, reason }) {
|
|
124
|
+
const message =
|
|
125
|
+
reason instanceof Error ? reason.message : String(reason ?? "");
|
|
126
|
+
console.error(
|
|
127
|
+
`fclt: cached runtime unavailable, falling back to Bun source entry (${message})`
|
|
128
|
+
);
|
|
129
|
+
const args = process.argv.slice(2);
|
|
130
|
+
const result = spawnSync("bun", [sourceEntry, ...args], {
|
|
131
|
+
stdio: "inherit",
|
|
132
|
+
env: {
|
|
133
|
+
...process.env,
|
|
134
|
+
FACULT_INSTALL_METHOD: "npm-source-fallback",
|
|
135
|
+
FACULT_NPM_PACKAGE_VERSION: version,
|
|
136
|
+
FACULT_SOURCE_ENTRY: sourceEntry,
|
|
137
|
+
FACULT_INSTALL_PM: packageManager,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (typeof result.status === "number") {
|
|
142
|
+
process.exit(result.status);
|
|
143
|
+
}
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
103
147
|
function resolveTarget() {
|
|
104
148
|
const platform = process.platform;
|
|
105
149
|
const arch = process.arch;
|
|
@@ -257,6 +301,14 @@ async function writeInstallState(state) {
|
|
|
257
301
|
await fsp.rename(`${installStatePath}.tmp`, installStatePath);
|
|
258
302
|
}
|
|
259
303
|
|
|
304
|
+
async function bestEffortWriteInstallState(state) {
|
|
305
|
+
try {
|
|
306
|
+
await writeInstallState(state);
|
|
307
|
+
} catch {
|
|
308
|
+
// Install state is useful metadata, but it should not block normal CLI usage.
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
260
312
|
main().catch((error) => {
|
|
261
313
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
262
314
|
console.error(message);
|
package/package.json
CHANGED
package/src/ai.ts
CHANGED
|
@@ -171,6 +171,7 @@ interface AddWritebackArgs {
|
|
|
171
171
|
summary: string;
|
|
172
172
|
asset?: string;
|
|
173
173
|
evidence?: WritebackEvidence[];
|
|
174
|
+
allowEmptyEvidence?: boolean;
|
|
174
175
|
confidence?: ConfidenceLevel;
|
|
175
176
|
source?: string;
|
|
176
177
|
suggestedDestination?: string;
|
|
@@ -348,6 +349,12 @@ export async function addWriteback(
|
|
|
348
349
|
args: AddWritebackArgs
|
|
349
350
|
): Promise<AiWritebackRecord> {
|
|
350
351
|
const homeDir = args.homeDir ?? process.env.HOME ?? "";
|
|
352
|
+
const evidence = args.evidence ?? [];
|
|
353
|
+
if (evidence.length === 0 && !args.allowEmptyEvidence) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
"writeback add requires at least one evidence item; pass --evidence <type:ref> or use --allow-empty-evidence for scratch/demo notes"
|
|
356
|
+
);
|
|
357
|
+
}
|
|
351
358
|
const scopeContext = resolveScopeContext(args.rootDir, homeDir);
|
|
352
359
|
const latest = await latestWritebackMap({
|
|
353
360
|
homeDir,
|
|
@@ -368,7 +375,7 @@ export async function addWriteback(
|
|
|
368
375
|
projectRoot: scopeContext.projectRoot,
|
|
369
376
|
kind: args.kind.trim(),
|
|
370
377
|
summary: args.summary.trim(),
|
|
371
|
-
evidence
|
|
378
|
+
evidence,
|
|
372
379
|
confidence: args.confidence ?? "medium",
|
|
373
380
|
source: args.source ?? "facult:manual",
|
|
374
381
|
assetRef: asset.assetRef,
|
|
@@ -669,6 +676,9 @@ export async function proposeEvolution(args: {
|
|
|
669
676
|
if (entry.status === "dismissed" || entry.status === "superseded") {
|
|
670
677
|
return false;
|
|
671
678
|
}
|
|
679
|
+
if (entry.evidence.length === 0) {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
672
682
|
if (filterAsset) {
|
|
673
683
|
return (
|
|
674
684
|
entry.assetId === filterAsset.assetId ||
|
|
@@ -1405,7 +1415,7 @@ function writebackHelp(): string {
|
|
|
1405
1415
|
return `fclt ai writeback
|
|
1406
1416
|
|
|
1407
1417
|
Usage:
|
|
1408
|
-
fclt ai writeback add --kind <kind> --summary <text> [--asset <selector>] [--tag <tag>] [--evidence <type:ref>]
|
|
1418
|
+
fclt ai writeback add --kind <kind> --summary <text> [--asset <selector>] [--tag <tag>] [--evidence <type:ref>] [--allow-empty-evidence]
|
|
1409
1419
|
fclt ai writeback list [--json]
|
|
1410
1420
|
fclt ai writeback show <id> [--json]
|
|
1411
1421
|
fclt ai writeback group --by <asset|kind|domain> [--json]
|
|
@@ -1521,6 +1531,7 @@ async function writebackCommand(argv: string[]) {
|
|
|
1521
1531
|
kind,
|
|
1522
1532
|
summary,
|
|
1523
1533
|
asset: parseStringFlag(parsed.argv, "--asset"),
|
|
1534
|
+
allowEmptyEvidence: parsed.argv.includes("--allow-empty-evidence"),
|
|
1524
1535
|
confidence:
|
|
1525
1536
|
(parseStringFlag(parsed.argv, "--confidence") as
|
|
1526
1537
|
| ConfidenceLevel
|
package/src/remote.ts
CHANGED
|
@@ -338,9 +338,11 @@ const BUILTIN_AUTOMATION_TEMPLATES: BuiltinAutomationTemplate[] = [
|
|
|
338
338
|
Use this memory for pattern continuity:
|
|
339
339
|
|
|
340
340
|
- Primary goal: convert repeated, evidence-backed session signal into durable writeback or evolution, not chat-only summary.
|
|
341
|
+
- For wide reviews, partition evidence by cwd first; do not let one repo's evidence stand in for another.
|
|
341
342
|
- Grounding: prefer evidence from session messages, tool calls, shell commands, diffs, tests, commits, and touched files.
|
|
342
343
|
- Threshold: only encode signal when you can name what was learned, why it matters, and the most plausible destination.
|
|
343
344
|
- Scope: default to project writeback unless the signal clearly belongs in global doctrine or a shared capability.
|
|
345
|
+
- Promote to global only when the same signal appears across multiple repos or clearly targets shared doctrine, shared agents, or shared skills.
|
|
344
346
|
- Verification: distinguish one-off friction from a repeated pattern before escalating it.
|
|
345
347
|
- If available, use [$feedback-loop-setup]({{feedbackLoopSkill}}) when the review needs stronger feedback loops or verification framing.
|
|
346
348
|
- If available, use [$capability-evolution]({{capabilityEvolutionSkill}}) when repeated signal should become a concrete proposal.
|
|
@@ -358,6 +360,7 @@ Before producing output:
|
|
|
358
360
|
|
|
359
361
|
Grounding rules:
|
|
360
362
|
- Work only from evidence in Codex sessions and nearby repo artifacts for the configured CWDs.
|
|
363
|
+
- Partition the review by cwd first. Name which configured cwds had real evidence this run and which did not.
|
|
361
364
|
- Prefer evidence from session messages, tool calls, shell commands, diffs, tests, commits, and touched files.
|
|
362
365
|
- Do not speculate about intent or propose changes that are not anchored in evidence.
|
|
363
366
|
- Distinguish one-off friction from repeated signal. Escalate only when the signal is durable enough to matter.
|
|
@@ -366,6 +369,7 @@ Decision rules:
|
|
|
366
369
|
- Use \`fclt ai writeback add\` when the signal, target asset, and scope are clear.
|
|
367
370
|
- Use \`fclt ai evolve\` only when repeated signal is strong enough to justify a reviewable capability change.
|
|
368
371
|
- Prefer project scope unless the learning clearly belongs in shared global doctrine, shared agents, shared skills, or other cross-project capability.
|
|
372
|
+
- For wide automations, require repeated evidence across more than one cwd before recommending a global/shared capability change unless the target is obviously global.
|
|
369
373
|
- Skip weak, speculative, or purely anecdotal observations.
|
|
370
374
|
|
|
371
375
|
Verification:
|
|
@@ -374,12 +378,77 @@ Verification:
|
|
|
374
378
|
- Separate missing context, weak verification, failed execution, and reusable pattern; do not collapse them together.
|
|
375
379
|
|
|
376
380
|
Output:
|
|
381
|
+
- Coverage: which cwds had concrete evidence, and which were effectively idle for this run.
|
|
377
382
|
- Recorded writebacks: what you recorded, why, and the target asset or command used.
|
|
378
383
|
- Evolution candidates: only the strongest repeated signals, with rationale and likely scope.
|
|
379
384
|
- Watch list: promising signals not yet strong enough to encode.
|
|
380
385
|
- Gaps in current operating model or verification harness: only if evidence supports them.
|
|
381
386
|
|
|
382
387
|
Keep the result concise, high-signal, and operational. If nothing crosses the threshold, say what you reviewed and why no writeback or evolution was justified.`,
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
id: "evolution-review",
|
|
391
|
+
title: "Evolution Review Loop",
|
|
392
|
+
description:
|
|
393
|
+
"Weekly Codex review of open evolution proposals and strong writeback clusters, with suggested next actions for review, acceptance, rejection, promotion, or apply.",
|
|
394
|
+
defaultRRule: "RRULE:FREQ=WEEKLY;BYHOUR=16;BYMINUTE=0;BYDAY=FR",
|
|
395
|
+
defaultStatus: "PAUSED",
|
|
396
|
+
defaultModel: "gpt-5.4",
|
|
397
|
+
defaultReasoningEffort: "high",
|
|
398
|
+
scope: "wide",
|
|
399
|
+
memory: `# Evolution Review Loop
|
|
400
|
+
|
|
401
|
+
Use this memory for continuity:
|
|
402
|
+
|
|
403
|
+
- Primary goal: keep proposal review moving so durable changes do not stall after writeback.
|
|
404
|
+
- Review continuity matters: track which proposals were already seen, what changed since the last review, and which action was previously recommended.
|
|
405
|
+
- Prefer reviewing existing proposal and writeback state over rediscovering the entire history from scratch.
|
|
406
|
+
- Scope: default to project evolution unless the proposal clearly belongs in shared doctrine, shared agents, shared skills, or another cross-project capability.
|
|
407
|
+
- For wide reviews, partition by cwd first and only recommend shared/global promotion when evidence truly spans multiple repos or the target asset is obviously shared.
|
|
408
|
+
- Recommend actions, do not silently apply high-risk changes.
|
|
409
|
+
- If available, use [$capability-evolution]({{capabilityEvolutionSkill}}) when proposal shaping or promotion decisions need stronger structure.
|
|
410
|
+
- If available, use [$feedback-loop-setup]({{feedbackLoopSkill}}) when proposal validity depends on weak or stale verification.
|
|
411
|
+
- If available, delegate bounded slices to \`evolution-planner\`, \`scope-promoter\`, \`writeback-curator\`, or \`verification-auditor\` when that materially improves rigor.
|
|
412
|
+
`,
|
|
413
|
+
prompt: `Goal: review current evolution state in the configured CWDs, keep proposal continuity intact, and suggest the highest-signal next actions for draft, review, accept, reject, promote, supersede, or apply.
|
|
414
|
+
|
|
415
|
+
Before producing output:
|
|
416
|
+
- Treat [AGENTS.md]({{codexAgents}}) as the rendered operating-model baseline for this Codex environment.
|
|
417
|
+
- Use [EVOLUTION.md]({{aiEvolution}}), [LEARNING_AND_WRITEBACK.md]({{aiLearningAndWriteback}}), and [VERIFICATION.md]({{aiVerification}}) as the doctrine for proposal quality, thresholding, and proof.
|
|
418
|
+
- Use [FEEDBACK_LOOPS.md]({{aiFeedbackLoops}}) when a proposal depends on a weak or gameable verification loop.
|
|
419
|
+
- If available, use [$capability-evolution]({{capabilityEvolutionSkill}}) when you need stronger proposal-shaping or promotion judgment.
|
|
420
|
+
- If available, use [$feedback-loop-setup]({{feedbackLoopSkill}}) when proposal validity depends on missing or stale verification.
|
|
421
|
+
- If it will materially improve quality, explicitly ask Codex to spawn narrow subagents such as \`evolution-planner\`, \`scope-promoter\`, \`writeback-curator\`, or \`verification-auditor\`. Only use them for bounded, non-overlapping review slices.
|
|
422
|
+
|
|
423
|
+
Grounding rules:
|
|
424
|
+
- Work from concrete proposal and writeback artifacts first, then confirm with nearby repo evidence when needed.
|
|
425
|
+
- Preserve continuity: compare this run against the automation memory and note what is actually new, unchanged, strengthened, weakened, accepted, rejected, or stale.
|
|
426
|
+
- Partition the review by cwd first. Name which configured cwds had real proposal or writeback state this run and which did not.
|
|
427
|
+
- Do not speculate about intent or recommend advancing a proposal without citing the evidence that still supports it.
|
|
428
|
+
- Distinguish proposal quality problems from execution gaps, stale evidence, missing verification, and simple lack of reviewer attention.
|
|
429
|
+
|
|
430
|
+
Decision rules:
|
|
431
|
+
- Prefer suggesting the next operator action over narrating the whole proposal history.
|
|
432
|
+
- Recommend \`draft\` when a proposal exists but is under-specified.
|
|
433
|
+
- Recommend \`review\` or \`accept\` only when the rationale, scope, and evidence are strong enough.
|
|
434
|
+
- Recommend \`apply\` only for already-accepted proposals whose evidence still looks valid and whose risk is appropriate.
|
|
435
|
+
- Recommend \`reject\` or \`supersede\` when the proposal is stale, contradicted, duplicated, or too weak.
|
|
436
|
+
- Recommend \`promote --to global\` only when a project-scoped proposal now clearly belongs in shared doctrine, shared agents, shared skills, or another cross-project surface.
|
|
437
|
+
- For wide automations, require repeated evidence across more than one cwd before recommending shared/global promotion unless the target is obviously global.
|
|
438
|
+
|
|
439
|
+
Verification:
|
|
440
|
+
- Verify every recommendation against at least one concrete artifact.
|
|
441
|
+
- Call out residual uncertainty instead of overstating confidence.
|
|
442
|
+
- If a proposal should move forward but the proof is weak, say exactly what verification is missing.
|
|
443
|
+
|
|
444
|
+
Output:
|
|
445
|
+
- Coverage: which cwds had concrete proposal/writeback evidence, and which were effectively idle for this run.
|
|
446
|
+
- Proposal queue: the strongest active proposals or proposal-worthy clusters, with what changed since the last review.
|
|
447
|
+
- Recommended actions: for each important item, the next operator action and why.
|
|
448
|
+
- Hold or reject: proposals that should stay parked, be rejected, or be superseded.
|
|
449
|
+
- Verification gaps: only the missing proof that materially blocks a recommendation.
|
|
450
|
+
|
|
451
|
+
Keep the result concise, continuity-aware, and operational. If nothing is ready to move, say what you reviewed and why no proposal should advance this run.`,
|
|
383
452
|
},
|
|
384
453
|
{
|
|
385
454
|
id: "tool-call-audit",
|
|
@@ -2874,7 +2943,7 @@ export async function templatesCommand(
|
|
|
2874
2943
|
const templateId = positional[0];
|
|
2875
2944
|
if (!templateId) {
|
|
2876
2945
|
console.error(
|
|
2877
|
-
"templates init automation requires a <template-id> (learning-review|tool-call-audit)"
|
|
2946
|
+
"templates init automation requires a <template-id> (learning-review|evolution-review|tool-call-audit)"
|
|
2878
2947
|
);
|
|
2879
2948
|
process.exitCode = 2;
|
|
2880
2949
|
return;
|