@hassangameryt/freeflow 0.8.0 → 0.9.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/.agents/plugins/marketplace.json +2 -2
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +3 -3
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +96 -149
- package/capabilities/cognitive-routing/SKILL.md +21 -129
- package/capabilities/cognitive-routing/references/coordinator.md +163 -0
- package/capabilities/cognitive-routing/references/worker.md +38 -0
- package/capabilities/compaction/SKILL.md +3 -3
- package/capabilities/compaction/references/summary-format.md +26 -15
- package/capabilities/messaging/SKILL.md +49 -0
- package/capabilities/task-memory/SKILL.md +87 -0
- package/command-surface.json +6 -54
- package/gemini-extension.json +1 -1
- package/hooks/claude/hooks.json +13 -3
- package/hooks/codex/hooks.json +2 -1
- package/hooks/shared/runtime-context.mjs +69 -77
- package/package.json +25 -8
- package/pi-extension/dist/ask-user/ask.js +108 -0
- package/pi-extension/dist/ask-user/form.js +250 -0
- package/pi-extension/dist/ask-user/questions.js +66 -0
- package/pi-extension/dist/ask-user/tool.js +124 -0
- package/pi-extension/dist/cognitive-routing/assembler.js +35 -11
- package/pi-extension/dist/cognitive-routing/bootstrap.js +90 -0
- package/pi-extension/dist/cognitive-routing/config.js +3 -4
- package/pi-extension/dist/cognitive-routing/event-store.js +4 -1
- package/pi-extension/dist/cognitive-routing/gate.js +20 -5
- package/pi-extension/dist/cognitive-routing/handlers.js +15 -13
- package/pi-extension/dist/cognitive-routing/model-control.js +17 -8
- package/pi-extension/dist/cognitive-routing/projection.js +23 -6
- package/pi-extension/dist/cognitive-routing/provenance.js +3 -1
- package/pi-extension/dist/cognitive-routing/render.js +15 -15
- package/pi-extension/dist/cognitive-routing/runtime.js +4 -2
- package/pi-extension/dist/cognitive-routing/schemas.js +4 -4
- package/pi-extension/dist/cognitive-routing/session.js +3 -1
- package/pi-extension/dist/cognitive-routing/sources.js +29 -4
- package/pi-extension/dist/cognitive-routing/state.js +18 -8
- package/pi-extension/dist/cognitive-routing/status.js +4 -3
- package/pi-extension/dist/cognitive-routing/tools.js +29 -15
- package/pi-extension/dist/cognitive-routing/types.js +10 -1
- package/pi-extension/dist/compaction/carry.js +36 -22
- package/pi-extension/dist/compaction/config.js +4 -2
- package/pi-extension/dist/compaction/controller.js +52 -29
- package/pi-extension/dist/compaction/harness.js +19 -3
- package/pi-extension/dist/compaction/results.js +14 -13
- package/pi-extension/dist/compaction/tool.js +6 -6
- package/pi-extension/dist/host/catalog.js +20 -35
- package/pi-extension/dist/host/config-validation.js +233 -0
- package/pi-extension/dist/host/config.js +78 -91
- package/pi-extension/dist/host/inbound.js +142 -0
- package/pi-extension/dist/host/projection-tags.js +1 -1
- package/pi-extension/dist/host/prompts.js +24 -13
- package/pi-extension/dist/host/read-only-session.js +6 -1
- package/pi-extension/dist/host/request-history.js +7 -3
- package/pi-extension/dist/host/rows.js +25 -0
- package/pi-extension/dist/host/runtime-state.js +17 -2
- package/pi-extension/dist/host/section-keeper.js +85 -0
- package/pi-extension/dist/host/settings/freeflow-command.js +846 -93
- package/pi-extension/dist/host/settings/settings-view.js +27 -8
- package/pi-extension/dist/host/status.js +19 -5
- package/pi-extension/dist/index.js +638 -72
- package/pi-extension/dist/messaging/config.js +27 -0
- package/pi-extension/dist/messaging/messaging.js +398 -0
- package/pi-extension/dist/messaging/registry.js +57 -0
- package/pi-extension/dist/messaging/render.js +90 -0
- package/pi-extension/dist/messaging/tools.js +109 -0
- package/pi-extension/dist/messaging/transport.js +101 -0
- package/pi-extension/dist/provider-support/cache/keepalive.js +16 -4
- package/pi-extension/dist/task-memory/config.js +49 -0
- package/pi-extension/dist/task-memory/registry.js +119 -0
- package/pi-extension/dist/task-memory/render.js +107 -0
- package/pi-extension/dist/task-memory/runtime.js +994 -0
- package/pi-extension/dist/task-memory/tool.js +233 -0
- package/pi-extension/dist/tool-execution/apply-patch/tool.js +1 -1
- package/pi-extension/dist/tool-execution/background.js +220 -28
- package/pi-extension/dist/tool-execution/config.js +14 -3
- package/pi-extension/dist/tool-execution/file-tracking.js +24 -4
- package/pi-extension/dist/tool-execution/guidance-notices.js +124 -0
- package/pi-extension/dist/tool-execution/skill-reads.js +143 -0
- package/pi-extension/dist/tool-execution/tools.js +2 -2
- package/runtime/prompts/cognitive-routing.md +4 -4
- package/runtime/prompts/core.md +24 -14
- package/runtime/prompts/interaction-contract.md +7 -4
- package/runtime/prompts/tool-execution.md +3 -16
- package/runtime/prompts/working-method.md +16 -99
- package/skills/deliver-work/SKILL.md +76 -0
- package/skills/deliver-work/references/branch-closeout.md +134 -0
- package/skills/deliver-work/references/commit.md +83 -0
- package/skills/deliver-work/references/launch.md +94 -0
- package/skills/deliver-work/references/release.md +85 -0
- package/skills/deliver-work/scripts/commit-state.mjs +139 -0
- package/skills/deliver-work/scripts/delivery-state.mjs +107 -0
- package/skills/deliver-work/scripts/lib/git-state.mjs +178 -0
- package/skills/deliver-work/scripts/push-route.mjs +122 -0
- package/skills/deliver-work/scripts/release-preflight.mjs +177 -0
- package/skills/discuss-work/SKILL.md +148 -0
- package/skills/{discuss → discuss-work}/references/discussion-continuity.md +10 -27
- package/skills/discuss-work/references/learning-action.md +36 -0
- package/skills/discuss-work/scripts/choices.mjs +101 -0
- package/skills/discuss-work/scripts/lib/choices.mjs +159 -0
- package/skills/evaluate-behavior/SKILL.md +107 -0
- package/skills/evaluate-behavior/references/authoring.md +381 -0
- package/skills/evaluate-behavior/references/coding-tasks.md +145 -0
- package/skills/evaluate-behavior/references/conversations-pipelines-judges.md +177 -0
- package/skills/evaluate-behavior/references/design-and-measurement.md +273 -0
- package/skills/evaluate-behavior/references/running-and-evidence.md +245 -0
- package/skills/evaluate-behavior/scripts/claude-guard.mjs +76 -0
- package/skills/evaluate-behavior/scripts/guard-rules.mjs +309 -0
- package/skills/evaluate-behavior/scripts/lib/agreement.mjs +90 -0
- package/skills/evaluate-behavior/scripts/lib/arms.mjs +55 -0
- package/skills/evaluate-behavior/scripts/lib/budget.mjs +87 -0
- package/skills/evaluate-behavior/scripts/lib/build-source.mjs +287 -0
- package/skills/evaluate-behavior/scripts/lib/clear.mjs +74 -0
- package/skills/evaluate-behavior/scripts/lib/container.mjs +395 -0
- package/skills/evaluate-behavior/scripts/lib/decision.mjs +418 -0
- package/skills/evaluate-behavior/scripts/lib/definitions.mjs +1072 -0
- package/skills/evaluate-behavior/scripts/lib/freeflow.mjs +103 -0
- package/skills/evaluate-behavior/scripts/lib/grade-command.mjs +591 -0
- package/skills/evaluate-behavior/scripts/lib/grade-comparison.mjs +90 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-interaction.mjs +19 -18
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-support.mjs +32 -5
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-workspace.mjs +28 -12
- package/skills/evaluate-behavior/scripts/lib/grade.mjs +101 -0
- package/skills/evaluate-behavior/scripts/lib/hooks.mjs +98 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/confinement.mjs +196 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/constraints.mjs +111 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/index.mjs +48 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/isolation.mjs +74 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/launch.mjs +123 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/model-call.mjs +73 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/recorder.mjs +178 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/session.mjs +494 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/skills.mjs +81 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/index.mjs +31 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/names.mjs +3 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/confinement.mjs +173 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/host.mjs +148 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/index.mjs +70 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/isolation.mjs +81 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/model-call.mjs +79 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/process.mjs +241 -0
- package/skills/{evaluate-skill/scripts/lib → evaluate-behavior/scripts/lib/hosts/pi}/rpc.mjs +144 -26
- package/skills/evaluate-behavior/scripts/lib/identity.mjs +82 -0
- package/skills/evaluate-behavior/scripts/lib/judge.mjs +688 -0
- package/skills/evaluate-behavior/scripts/lib/metrics.mjs +332 -0
- package/skills/evaluate-behavior/scripts/lib/model-call.mjs +17 -0
- package/skills/evaluate-behavior/scripts/lib/participants.mjs +616 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/process.mjs +22 -0
- package/skills/evaluate-behavior/scripts/lib/queue.mjs +54 -0
- package/skills/evaluate-behavior/scripts/lib/reference.mjs +343 -0
- package/skills/evaluate-behavior/scripts/lib/reuse.mjs +277 -0
- package/skills/evaluate-behavior/scripts/lib/review.mjs +933 -0
- package/skills/evaluate-behavior/scripts/lib/runner.mjs +732 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/sandbox.mjs +119 -14
- package/skills/evaluate-behavior/scripts/lib/scan-shell.mjs +457 -0
- package/skills/evaluate-behavior/scripts/lib/scan.mjs +479 -0
- package/skills/evaluate-behavior/scripts/lib/seal.mjs +91 -0
- package/skills/evaluate-behavior/scripts/lib/shell.mjs +560 -0
- package/skills/evaluate-behavior/scripts/lib/source-override.mjs +64 -0
- package/skills/evaluate-behavior/scripts/lib/subject-file.mjs +88 -0
- package/skills/{evaluate-skill/scripts/lib/pi.mjs → evaluate-behavior/scripts/lib/subject.mjs} +235 -265
- package/skills/evaluate-behavior/scripts/lib/task-copy.mjs +124 -0
- package/skills/evaluate-behavior/scripts/lib/trials.mjs +524 -0
- package/skills/evaluate-behavior/scripts/lib/validity.mjs +68 -0
- package/skills/evaluate-behavior/scripts/lib/view-html.mjs +454 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/view.mjs +159 -18
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-command-tool.mjs +21 -6
- package/skills/evaluate-behavior/scripts/pi-container-route.mjs +23 -0
- package/skills/evaluate-behavior/scripts/pi-guard.mjs +77 -0
- package/skills/evaluate-behavior/scripts/pi-login-refresh.mjs +31 -0
- package/skills/evaluate-behavior/scripts/skill-eval.mjs +586 -0
- package/skills/execute-work/SKILL.md +96 -104
- package/skills/execute-work/references/code-practices.md +27 -51
- package/skills/execute-work/references/design.md +271 -0
- package/skills/execute-work/references/diagnose.md +117 -0
- package/skills/execute-work/references/migration.md +108 -0
- package/skills/execute-work/references/simplify.md +65 -0
- package/skills/execute-work/scripts/self-review.mjs +422 -0
- package/skills/execute-work/scripts/touch-map.mjs +290 -0
- package/skills/review-work/SKILL.md +89 -65
- package/skills/review-work/references/adjudication.md +94 -0
- package/skills/review-work/references/artifact.md +198 -0
- package/skills/review-work/references/evals-and-judges.md +66 -0
- package/skills/review-work/references/work.md +177 -0
- package/skills/review-work/scripts/check-brief.mjs +128 -0
- package/skills/review-work/scripts/review-packet.mjs +233 -0
- package/skills/setup-freeflow/SKILL.md +68 -17
- package/skills/track-work/SKILL.md +52 -20
- package/skills/track-work/references/handoff.md +190 -0
- package/skills/track-work/references/projects.md +51 -0
- package/skills/track-work/references/working-record-format.md +36 -18
- package/skills/track-work/scripts/lib/document.mjs +17 -13
- package/skills/track-work/scripts/lib/format.mjs +2 -0
- package/skills/track-work/scripts/lib/recover.mjs +254 -0
- package/skills/track-work/scripts/lib/registry.mjs +656 -0
- package/skills/track-work/scripts/lib/search.mjs +40 -0
- package/skills/track-work/scripts/lib/store.mjs +46 -15
- package/skills/track-work/scripts/lib/views.mjs +50 -1
- package/skills/track-work/scripts/working-record.mjs +232 -13
- package/skills/verify-work/SKILL.md +45 -37
- package/skills/verify-work/references/feature-map.md +120 -0
- package/skills/verify-work/references/runtime-evidence.md +203 -0
- package/skills/verify-work/references/test-design.md +25 -6
- package/skills/verify-work/scripts/test-changes.mjs +246 -0
- package/skills/write-docs/SKILL.md +113 -25
- package/skills/write-docs/references/documentation-shapes.md +68 -3
- package/skills/write-docs/references/spec-and-plan-shapes.md +308 -0
- package/skills/write-docs/scripts/artifact-check.mjs +148 -0
- package/skills/write-docs/scripts/docs-check.mjs +96 -6
- package/skills/write-docs/scripts/plan-lint.mjs +123 -0
- package/skills/write-skill/SKILL.md +92 -52
- package/skills/write-skill/references/activation-and-wording.md +143 -0
- package/skills/write-skill/references/placement-and-dependencies.md +154 -0
- package/skills/write-skill/scripts/lib/compare.mjs +701 -0
- package/skills/write-skill/scripts/lib/skill-author.mjs +197 -3
- package/skills/write-skill/scripts/skill-author.mjs +51 -8
- package/capabilities/cognitive-routing/references/both-mode.md +0 -36
- package/capabilities/cognitive-routing/references/executor-mode.md +0 -48
- package/capabilities/cognitive-routing/references/helper-mode.md +0 -47
- package/plugin.json +0 -33
- package/skills/action-selection/SKILL.md +0 -92
- package/skills/action-selection/references/trajectory-stalls.md +0 -73
- package/skills/bypass/SKILL.md +0 -153
- package/skills/commit-work/SKILL.md +0 -93
- package/skills/commit-work/references/git-state-edges.md +0 -94
- package/skills/commit-work/references/simple-push.md +0 -94
- package/skills/commit-work/references/staging-decisions.md +0 -106
- package/skills/decision-gate/SKILL.md +0 -87
- package/skills/design-for-depth/SKILL.md +0 -108
- package/skills/design-for-depth/references/design-pressure-signals.md +0 -84
- package/skills/design-for-depth/references/implementation-decisions.md +0 -76
- package/skills/design-for-depth/references/interface-design-loop.md +0 -105
- package/skills/design-for-depth/references/module-and-dependency-design.md +0 -117
- package/skills/design-for-depth/references/state-and-failure-boundaries.md +0 -125
- package/skills/diagnose-failure/SKILL.md +0 -140
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +0 -50
- package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +0 -51
- package/skills/discuss/SKILL.md +0 -140
- package/skills/evaluate-skill/SKILL.md +0 -188
- package/skills/evaluate-skill/references/evaluation-definition-schema.md +0 -415
- package/skills/evaluate-skill/references/evaluation-design.md +0 -112
- package/skills/evaluate-skill/references/execution-and-evidence.md +0 -111
- package/skills/evaluate-skill/references/review-and-revision.md +0 -94
- package/skills/evaluate-skill/scripts/lib/definitions.mjs +0 -448
- package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +0 -60
- package/skills/evaluate-skill/scripts/lib/grade.mjs +0 -64
- package/skills/evaluate-skill/scripts/lib/review.mjs +0 -196
- package/skills/evaluate-skill/scripts/lib/runner.mjs +0 -405
- package/skills/evaluate-skill/scripts/lib/trials.mjs +0 -103
- package/skills/evaluate-skill/scripts/pi-guard.mjs +0 -181
- package/skills/evaluate-skill/scripts/skill-eval.mjs +0 -182
- package/skills/execute-work/references/execute-work-edges.md +0 -78
- package/skills/finish-branch/SKILL.md +0 -117
- package/skills/finish-branch/references/integration-options.md +0 -80
- package/skills/handoff/SKILL.md +0 -82
- package/skills/handoff/references/handoff-templates.md +0 -150
- package/skills/launch-work/SKILL.md +0 -116
- package/skills/launch-work/references/launch-readiness.md +0 -85
- package/skills/migration-work/SKILL.md +0 -115
- package/skills/migration-work/references/migration-lifecycle.md +0 -82
- package/skills/release-work/SKILL.md +0 -118
- package/skills/release-work/references/release-evidence.md +0 -78
- package/skills/review-artifact/SKILL.md +0 -143
- package/skills/review-artifact/references/adjudicate-artifact-review.md +0 -101
- package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +0 -159
- package/skills/review-work/references/adjudicate-work-review.md +0 -97
- package/skills/review-work/references/independent-work-reviewer-contract.md +0 -139
- package/skills/review-work/references/security-risk-lens.md +0 -67
- package/skills/setup-freeflow/references/activation-contract.md +0 -103
- package/skills/setup-freeflow/references/host-setup.md +0 -67
- package/skills/simplify-code/SKILL.md +0 -85
- package/skills/simplify-code/references/simplification-patterns.md +0 -70
- package/skills/verify-work/references/browser-runtime-evidence.md +0 -51
- package/skills/verify-work/references/integration-evidence.md +0 -49
- package/skills/verify-work/references/performance-evidence.md +0 -59
- package/skills/workflow/SKILL.md +0 -171
- package/skills/workflow/references/domain-skill-composition.md +0 -45
- package/skills/write-plan/SKILL.md +0 -92
- package/skills/write-plan/references/plan-shapes.md +0 -156
- package/skills/write-skill/references/activation-boundaries.md +0 -106
- package/skills/write-skill/references/agent-first-instructions.md +0 -133
- package/skills/write-skill/references/progressive-disclosure.md +0 -129
- package/skills/write-skill/references/skill-dependency-graphs.md +0 -192
- package/skills/write-spec/SKILL.md +0 -85
- package/skills/write-spec/references/artifact-standards.md +0 -86
- package/skills/write-spec/references/decision-records.md +0 -80
- package/skills/write-spec/references/spec-shapes.md +0 -143
- /package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/evidence.mjs +0 -0
- /package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-observer.mjs +0 -0
package/hooks/claude/hooks.json
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "Load Freeflow core runtime context in Claude Code.",
|
|
2
|
+
"description": "Load Freeflow core runtime context in Claude Code, one hook per prompt: Claude Code caps each hook's context at 10,000 characters.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"SessionStart": [
|
|
5
5
|
{
|
|
6
|
-
"matcher": "startup|resume|clear|compact",
|
|
6
|
+
"matcher": "startup|resume|clear|compact|fork",
|
|
7
7
|
"hooks": [
|
|
8
8
|
{
|
|
9
9
|
"type": "command",
|
|
10
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/adapters/claude-session-start.mjs\" SessionStart",
|
|
10
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/adapters/claude-session-start.mjs\" SessionStart core",
|
|
11
11
|
"timeout": 5,
|
|
12
12
|
"statusMessage": "Loading Freeflow context"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"type": "command",
|
|
16
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/adapters/claude-session-start.mjs\" SessionStart interactionContract",
|
|
17
|
+
"timeout": 5
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/adapters/claude-session-start.mjs\" SessionStart workingMethod",
|
|
22
|
+
"timeout": 5
|
|
13
23
|
}
|
|
14
24
|
]
|
|
15
25
|
}
|
package/hooks/codex/hooks.json
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"type": "command",
|
|
10
10
|
"command": "node \"${PLUGIN_ROOT}/hooks/adapters/codex-session-start.mjs\" SessionStart",
|
|
11
11
|
"timeout": 5,
|
|
12
|
-
"statusMessage": "Loading Freeflow context"
|
|
12
|
+
"statusMessage": "Loading Freeflow context",
|
|
13
|
+
"additionalContextLimit": 8000
|
|
13
14
|
}
|
|
14
15
|
]
|
|
15
16
|
}
|
|
@@ -2,6 +2,16 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
// The validator Pi uses, so a config is valid or invalid on every host alike.
|
|
6
|
+
import {
|
|
7
|
+
resolveReviewSettings,
|
|
8
|
+
reviewStateLine,
|
|
9
|
+
selectedRuntimePrompts,
|
|
10
|
+
skillsOff,
|
|
11
|
+
skillsStateLine,
|
|
12
|
+
validateLocalConfig,
|
|
13
|
+
validateRepositoryConfig,
|
|
14
|
+
} from "../../pi-extension/dist/host/config-validation.js";
|
|
5
15
|
|
|
6
16
|
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
7
17
|
export const PLUGIN_ROOT = path.resolve(SCRIPT_DIR, "../..");
|
|
@@ -62,57 +72,6 @@ function readConfigLayer(filePath, validate) {
|
|
|
62
72
|
}
|
|
63
73
|
}
|
|
64
74
|
|
|
65
|
-
function isRecord(value) {
|
|
66
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function removedContextConfigError(value, filePath) {
|
|
70
|
-
const removedKeys = ["contextVirtualization", "conversationHistory"].filter((key) => Object.hasOwn(value, key));
|
|
71
|
-
return removedKeys.length > 0
|
|
72
|
-
? `Removed Freeflow context setting(s) in ${filePath}: ${removedKeys.join(", ")}. Delete those keys before using Freeflow.`
|
|
73
|
-
: null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function validateCoreConfigFields(value) {
|
|
77
|
-
if (Object.hasOwn(value, "enabled") && typeof value.enabled !== "boolean") {
|
|
78
|
-
return "enabled must be a boolean";
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function isValidSetupConfig(value) {
|
|
84
|
-
if (!isRecord(value)) return "repository config must be a JSON object";
|
|
85
|
-
|
|
86
|
-
const removedContextError = removedContextConfigError(value, ".freeflow/config.json");
|
|
87
|
-
if (removedContextError) return removedContextError;
|
|
88
|
-
|
|
89
|
-
const allowedKeys = new Set(["enabled", "outputRouter", "observedRouting", "scriptTransform", "cognitiveRouting"]);
|
|
90
|
-
if (!Object.keys(value).every((key) => allowedKeys.has(key))) {
|
|
91
|
-
return "repository config contains unsupported top-level keys";
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const coreError = validateCoreConfigFields(value);
|
|
95
|
-
if (coreError) return coreError;
|
|
96
|
-
|
|
97
|
-
for (const key of ["outputRouter", "observedRouting", "scriptTransform"]) {
|
|
98
|
-
if (Object.hasOwn(value, key) && !isRecord(value[key])) return `${key} must be an object`;
|
|
99
|
-
}
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function isValidLocalConfig(value) {
|
|
104
|
-
if (!isRecord(value)) return "local config must be a JSON object";
|
|
105
|
-
|
|
106
|
-
const removedContextError = removedContextConfigError(value, ".freeflow/local.json");
|
|
107
|
-
if (removedContextError) return removedContextError;
|
|
108
|
-
|
|
109
|
-
const allowedKeys = new Set(["enabled", "processing", "cognitiveRouting"]);
|
|
110
|
-
if (!Object.keys(value).every((key) => allowedKeys.has(key))) {
|
|
111
|
-
return "local config contains unsupported top-level keys";
|
|
112
|
-
}
|
|
113
|
-
return validateCoreConfigFields(value);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
75
|
function resolveLayeredValue(repository, local, key, fallback) {
|
|
117
76
|
if (Object.hasOwn(local, key)) return local[key];
|
|
118
77
|
if (Object.hasOwn(repository, key)) return repository[key];
|
|
@@ -124,8 +83,8 @@ function resolveCoreConfig(repository, local) {
|
|
|
124
83
|
}
|
|
125
84
|
|
|
126
85
|
export function readConfig(root) {
|
|
127
|
-
const repository = readConfigLayer(path.join(root, ".freeflow", "config.json"),
|
|
128
|
-
const local = readConfigLayer(path.join(root, ".freeflow", "local.json"),
|
|
86
|
+
const repository = readConfigLayer(path.join(root, ".freeflow", "config.json"), validateRepositoryConfig);
|
|
87
|
+
const local = readConfigLayer(path.join(root, ".freeflow", "local.json"), validateLocalConfig);
|
|
129
88
|
const localValid = !local.exists || local.valid;
|
|
130
89
|
const configured = repository.valid && localValid;
|
|
131
90
|
const core = resolveCoreConfig(repository.valid ? repository.parsed : {}, local.valid ? local.parsed : {});
|
|
@@ -139,42 +98,67 @@ export function readConfig(root) {
|
|
|
139
98
|
localExists: local.exists,
|
|
140
99
|
localValid,
|
|
141
100
|
error: repository.valid ? local.error : repository.error,
|
|
101
|
+
errorPath: repository.valid ? ".freeflow/local.json" : ".freeflow/config.json",
|
|
142
102
|
enabled,
|
|
103
|
+
prompts: selectedRuntimePrompts(repository.parsed, local.parsed),
|
|
104
|
+
review: resolveReviewSettings(repository.parsed, local.parsed),
|
|
105
|
+
skillsOff: skillsOff(repository.parsed, local.parsed),
|
|
143
106
|
};
|
|
144
107
|
}
|
|
145
108
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
109
|
+
const PROMPT_FILES = {
|
|
110
|
+
core: "core.md",
|
|
111
|
+
interactionContract: "interaction-contract.md",
|
|
112
|
+
workingMethod: "working-method.md",
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The selected prompts in Pi's order, or only `part` when a host delivers each prompt through its own hook. A selected
|
|
117
|
+
* core or Interaction Contract must exist; the Working Method is optional, as on Pi.
|
|
118
|
+
*/
|
|
119
|
+
function loadRuntimeContext(selection, part) {
|
|
120
|
+
const prompts = [];
|
|
121
|
+
for (const [key, file] of Object.entries(PROMPT_FILES)) {
|
|
122
|
+
if (!selection[key] || (part && key !== part)) continue;
|
|
123
|
+
const text = readText(path.join(PLUGIN_ROOT, "runtime", "prompts", file));
|
|
124
|
+
if (!text && key !== "workingMethod") throw new Error("Freeflow core runtime prompt files are missing.");
|
|
125
|
+
if (text) prompts.push(text);
|
|
152
126
|
}
|
|
153
|
-
|
|
154
|
-
return { corePrompt, interactionContract };
|
|
127
|
+
return prompts;
|
|
155
128
|
}
|
|
156
129
|
|
|
157
|
-
|
|
158
|
-
|
|
130
|
+
// The review and skills lines are Pi's, and as on Pi only a choice the user made adds them. This host loads plugin
|
|
131
|
+
// skills itself, so the skills line is how an off skill reaches the agent here.
|
|
132
|
+
function runtimeStateContext(config) {
|
|
133
|
+
const lines = [reviewStateLine(config.review), skillsStateLine(config.skillsOff)].filter(Boolean);
|
|
134
|
+
return ["# Freeflow Runtime State", "", "Freeflow: active", ...lines].join("\n");
|
|
159
135
|
}
|
|
160
136
|
|
|
161
|
-
|
|
137
|
+
/**
|
|
138
|
+
* The session-start context for the repository at `root`: every selected prompt, or one `part` of it (`core`,
|
|
139
|
+
* `interactionContract`, `workingMethod`). Claude Code caps each hook's context at 10,000 characters, so it runs one
|
|
140
|
+
* hook per part. A configuration error and the Runtime State, which the core prompt refers to, travel with the core.
|
|
141
|
+
*/
|
|
142
|
+
export function renderRuntimeContext(root, part = undefined) {
|
|
143
|
+
if (part !== undefined && !Object.hasOwn(PROMPT_FILES, part)) return "";
|
|
162
144
|
const config = readConfig(root);
|
|
163
145
|
if (!config.valid) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
146
|
+
// A missing repository config means Freeflow is not set up here: stay silent. An existing invalid file is reported,
|
|
147
|
+
// as Pi's status does, so Freeflow is not silently missing.
|
|
148
|
+
if (!config.error || (part && part !== "core")) return "";
|
|
149
|
+
return [
|
|
150
|
+
"# Freeflow Configuration Error",
|
|
151
|
+
"",
|
|
152
|
+
`Freeflow is inactive because ${config.errorPath} is invalid: ${config.error}`,
|
|
153
|
+
`Fix ${config.errorPath} or use the setup-freeflow skill, then start a new session.`,
|
|
154
|
+
].join("\n");
|
|
173
155
|
}
|
|
174
156
|
if (!config.enabled) return "";
|
|
175
157
|
|
|
176
|
-
|
|
177
|
-
|
|
158
|
+
// The user chooses the prompts on hosts other than Pi; with none selected there is nothing to deliver.
|
|
159
|
+
const prompts = loadRuntimeContext(config.prompts, part);
|
|
160
|
+
if (config.prompts.core && (!part || part === "core")) prompts.push(runtimeStateContext(config));
|
|
161
|
+
return prompts.join("\n\n");
|
|
178
162
|
}
|
|
179
163
|
|
|
180
164
|
export function emitHookOutput(output) {
|
|
@@ -188,7 +172,8 @@ export function runSessionStartAdapter({ eventNames, getWorkspaceCwd, formatOutp
|
|
|
188
172
|
if (!eventNames.includes(eventName)) return;
|
|
189
173
|
|
|
190
174
|
const root = findWorkspaceRoot(getWorkspaceCwd(input));
|
|
191
|
-
|
|
175
|
+
// An optional third argument names the one prompt this hook delivers.
|
|
176
|
+
const additionalContext = renderRuntimeContext(root, process.argv[3]);
|
|
192
177
|
if (additionalContext.trim()) {
|
|
193
178
|
emitHookOutput(formatOutput({ eventName, input, additionalContext }));
|
|
194
179
|
}
|
|
@@ -204,5 +189,12 @@ export function runSafely(run) {
|
|
|
204
189
|
}
|
|
205
190
|
|
|
206
191
|
export function isMainModule(metaUrl) {
|
|
207
|
-
|
|
192
|
+
if (!process.argv[1]) return false;
|
|
193
|
+
// Compare real paths: Node resolves symlinks in import.meta.url but not in argv, and a host may run the plugin from a
|
|
194
|
+
// symlinked directory (macOS /var is /private/var).
|
|
195
|
+
try {
|
|
196
|
+
return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(metaUrl));
|
|
197
|
+
} catch {
|
|
198
|
+
return pathToFileURL(path.resolve(process.argv[1])).href === metaUrl;
|
|
199
|
+
}
|
|
208
200
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hassangameryt/freeflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Feedback-based control system for coding agents.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -22,31 +22,46 @@
|
|
|
22
22
|
"build:pi-extension": "node -e \"require('node:fs').rmSync('pi-extension/dist', { recursive: true, force: true })\" && tsc -p pi-extension/tsconfig.json && npm run format:pi-extension-dist",
|
|
23
23
|
"build": "npm run build:pi-extension",
|
|
24
24
|
"typecheck": "tsc -p pi-extension/tsconfig.json --noEmit",
|
|
25
|
-
"
|
|
25
|
+
"lint": "eslint pi-extension/src",
|
|
26
|
+
"check": "npm run typecheck && npm run lint && npm run check:source && npm run check:host-manifests && npm run check:changelog && npm run check:release-workflow && npm run check:docs && npm run check:dev-docs && npm run format:check && npm run check:release-version && npm run check:commands && npm run check:activation && npm run check:runtime && npm run check:package && npm run check:eval-coverage && npm run check:eval-definitions && npm run check:coverage-matrix && npm run check:skill-routing && npm run check:incident-fixtures",
|
|
26
27
|
"check:source": "node scripts/validation/check-source-files.mjs",
|
|
27
28
|
"check:host-manifests": "node scripts/validation/check-host-manifests.mjs",
|
|
28
29
|
"check:changelog": "node scripts/validation/check-changelog.mjs --structure",
|
|
29
30
|
"check:release-workflow": "node scripts/validation/check-release-workflow.mjs",
|
|
30
31
|
"check:docs": "node scripts/validation/check-docs.mjs",
|
|
32
|
+
"check:dev-docs": "node skills/write-docs/scripts/docs-check.mjs --strict dev-docs",
|
|
31
33
|
"check:release-version": "node scripts/validation/check-release-version.mjs",
|
|
32
34
|
"check:commands": "scripts/validation/audit-command-surface.sh",
|
|
33
35
|
"check:activation": "scripts/validation/check-activation-contract.sh",
|
|
34
36
|
"check:runtime": "hooks/tests/check-runtime-context-hook.sh",
|
|
35
37
|
"check:package": "node scripts/validation/check-package.mjs",
|
|
36
|
-
"check:eval-coverage": "node scripts/validation/check-eval-coverage.mjs",
|
|
38
|
+
"check:eval-coverage": "node scripts/validation/check-eval-coverage.mjs --strict",
|
|
39
|
+
"check:eval-definitions": "node scripts/validation/check-eval-definitions.mjs",
|
|
40
|
+
"check:coverage-matrix": "node .skill-eval/scripts/verify-coverage-matrix.mjs",
|
|
41
|
+
"check:skill-routing": "node scripts/validation/check-skill-routing-doc.mjs",
|
|
42
|
+
"check:incident-fixtures": "node .skill-eval/workflow/scripts/verify-incident-fixtures.mjs",
|
|
37
43
|
"check:release-metadata": "scripts/validation/validate-release-metadata.sh --mode prepublish",
|
|
38
44
|
"perf:request": "node scripts/perf/request-overhead.mjs",
|
|
45
|
+
"eval:gate": "node .skill-eval/scripts/gate.mjs",
|
|
46
|
+
"test": "npm run test:pi-extension && npm run test:host-manifests && npm run test:skill-author && npm run test:skill-eval && npm run test:track-work && npm run test:discuss-work && npm run test:execute-work && npm run test:deliver-work && npm run test:review-work && npm run test:verify-work && npm run test:write-docs && npm run test:freeflow-snapshot && npm run test:release-utils && npm run test:release-workflow && npm run test:changelog && npm run test:docs && npm run test:eval-coverage && npm run test:guards",
|
|
39
47
|
"test:pi-extension": "npm run build && node --test $(find pi-extension/tests -name '*.test.js' | sort)",
|
|
40
48
|
"test:host-manifests": "node --test scripts/validation/check-host-manifests.test.mjs hooks/tests/check-host-adapters.test.mjs",
|
|
41
49
|
"test:skill-author": "node --test $(find .skill-eval/write-skill/tests -name '*.test.mjs' | sort)",
|
|
42
|
-
"test:skill-eval": "node --test $(find .skill-eval/evaluate-
|
|
50
|
+
"test:skill-eval": "node --test $(find .skill-eval/evaluate-behavior/tests -name '*.test.mjs' | sort)",
|
|
43
51
|
"test:track-work": "node --test .skill-eval/track-work/tests/*.test.mjs",
|
|
52
|
+
"test:discuss-work": "node --test .skill-eval/discuss-work/tests/*.test.mjs",
|
|
53
|
+
"test:execute-work": "node --test .skill-eval/execute-work/tests/*.test.mjs",
|
|
54
|
+
"test:deliver-work": "node --test .skill-eval/deliver-work/tests/*.test.mjs",
|
|
55
|
+
"test:review-work": "node --test .skill-eval/review-work/tests/*.test.mjs",
|
|
56
|
+
"test:verify-work": "node --test .skill-eval/verify-work/tests/*.test.mjs",
|
|
57
|
+
"test:write-docs": "node --test .skill-eval/write-docs/tests/*.test.mjs",
|
|
44
58
|
"test:freeflow-snapshot": "node --test scripts/freeflow-snapshot.test.mjs",
|
|
45
59
|
"test:release-utils": "node --test scripts/release-utils.test.mjs",
|
|
46
60
|
"test:changelog": "node --test scripts/validation/check-changelog.test.mjs",
|
|
47
61
|
"test:release-workflow": "node --test scripts/validation/check-release-workflow.test.mjs",
|
|
48
62
|
"test:docs": "node --test scripts/validation/check-docs.test.mjs",
|
|
49
63
|
"test:eval-coverage": "node --test scripts/validation/check-eval-coverage.test.mjs",
|
|
64
|
+
"test:guards": "node --test scripts/validation/guards.test.mjs",
|
|
50
65
|
"release:prepare": "node scripts/release.mjs",
|
|
51
66
|
"release:notes": "node scripts/release-notes.mjs",
|
|
52
67
|
"snapshot:refresh": "node scripts/freeflow-snapshot.mjs"
|
|
@@ -82,7 +97,6 @@
|
|
|
82
97
|
"image": "https://raw.githubusercontent.com/hassan-mohiddin/freeflow/main/assets/freeflow-plugin-icon-transparent.png"
|
|
83
98
|
},
|
|
84
99
|
"files": [
|
|
85
|
-
"plugin.json",
|
|
86
100
|
"gemini-extension.json",
|
|
87
101
|
".agents/**",
|
|
88
102
|
".claude-plugin/**",
|
|
@@ -113,11 +127,14 @@
|
|
|
113
127
|
"@earendil-works/pi-tui": ">=0.99.1"
|
|
114
128
|
},
|
|
115
129
|
"devDependencies": {
|
|
116
|
-
"@earendil-works/pi-coding-agent": "1.0
|
|
117
|
-
"@earendil-works/pi-tui": "1.0
|
|
130
|
+
"@earendil-works/pi-coding-agent": "1.4.0",
|
|
131
|
+
"@earendil-works/pi-tui": "1.4.0",
|
|
132
|
+
"@eslint/js": "10.0.1",
|
|
118
133
|
"@types/node": "^22.15.30",
|
|
134
|
+
"eslint": "10.12.0",
|
|
119
135
|
"prettier": "3.9.5",
|
|
120
|
-
"typescript": "^5.8.3"
|
|
136
|
+
"typescript": "^5.8.3",
|
|
137
|
+
"typescript-eslint": "8.71.1"
|
|
121
138
|
},
|
|
122
139
|
"dependencies": {}
|
|
123
140
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { QuestionForm } from "./form.js";
|
|
2
|
+
/*
|
|
3
|
+
* Putting questions to the user in whatever UI the session has:
|
|
4
|
+
* - the terminal: one QuestionForm (descriptions, own answer, notes, multi-select);
|
|
5
|
+
* - RPC clients: one `select` per question (option and description on one line) and an `input` for the user's own
|
|
6
|
+
* answer; multi-select repeats `select` until the user picks Done;
|
|
7
|
+
* - no UI (print, JSON): unavailable, so the caller asks in its reply.
|
|
8
|
+
* Closing any dialog without answering dismisses the whole question.
|
|
9
|
+
*/
|
|
10
|
+
const OWN = "Type my own answer";
|
|
11
|
+
const DONE = "Done";
|
|
12
|
+
export async function askUser(ctx, questions, signal, form = {}) {
|
|
13
|
+
if (!ctx?.hasUI || !ctx.ui) return { status: "unavailable", reason: "No one can answer a dialog in this session." };
|
|
14
|
+
if (ctx.mode === "tui" && typeof ctx.ui.custom === "function") {
|
|
15
|
+
if (signal?.aborted) return { status: "dismissed" };
|
|
16
|
+
let timer;
|
|
17
|
+
let onAbort;
|
|
18
|
+
const outcome = await ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
19
|
+
const view = new QuestionForm(questions, theme, done, () => tui?.requestRender?.(), form);
|
|
20
|
+
// Pi's custom dialog takes no signal: an aborted call (the user pressed Escape on the turn) closes it here.
|
|
21
|
+
if (signal) {
|
|
22
|
+
onAbort = () => done({ status: "dismissed" });
|
|
23
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
24
|
+
}
|
|
25
|
+
// A dialog with a deadline counts down and closes itself unanswered when it passes.
|
|
26
|
+
if (form.deadline !== undefined)
|
|
27
|
+
timer = setInterval(() => {
|
|
28
|
+
if (Date.now() >= form.deadline) done({ status: "dismissed" });
|
|
29
|
+
else {
|
|
30
|
+
view.invalidate();
|
|
31
|
+
tui?.requestRender?.();
|
|
32
|
+
}
|
|
33
|
+
}, 1000);
|
|
34
|
+
return view;
|
|
35
|
+
});
|
|
36
|
+
if (timer) clearInterval(timer);
|
|
37
|
+
if (onAbort) signal?.removeEventListener("abort", onAbort);
|
|
38
|
+
return outcome ?? { status: "dismissed" };
|
|
39
|
+
}
|
|
40
|
+
const answers = [];
|
|
41
|
+
for (const question of questions) {
|
|
42
|
+
const answer = await askOne(ctx, question, signal);
|
|
43
|
+
if (!answer) return { status: "dismissed" };
|
|
44
|
+
answers.push(answer);
|
|
45
|
+
}
|
|
46
|
+
return { status: "answered", answers };
|
|
47
|
+
}
|
|
48
|
+
async function askOne(ctx, question, signal) {
|
|
49
|
+
const title = `${question.header} · ${question.question}`;
|
|
50
|
+
const labels = question.options.map((option) =>
|
|
51
|
+
option.description ? `${option.label} — ${option.description}` : option.label,
|
|
52
|
+
);
|
|
53
|
+
const base = { header: question.header, question: question.question };
|
|
54
|
+
const own = async () => {
|
|
55
|
+
const text = (await ctx.ui.input(title, "Your answer", signal ? { signal } : undefined))?.trim();
|
|
56
|
+
return text ? { ...base, selected: [], text } : undefined;
|
|
57
|
+
};
|
|
58
|
+
if (!question.multiSelect) {
|
|
59
|
+
const choice = await ctx.ui.select(title, [...labels, OWN], signal ? { signal } : undefined);
|
|
60
|
+
if (choice === undefined) return undefined;
|
|
61
|
+
if (choice === OWN) return own();
|
|
62
|
+
return { ...base, selected: [question.options[labels.indexOf(choice)].label] };
|
|
63
|
+
}
|
|
64
|
+
const picked = new Set();
|
|
65
|
+
for (;;) {
|
|
66
|
+
const shown = labels.map((label, at) => `${picked.has(at) ? "[x]" : "[ ]"} ${label}`);
|
|
67
|
+
const choice = await ctx.ui.select(
|
|
68
|
+
`${title} (choose one or more, then ${DONE})`,
|
|
69
|
+
[...shown, OWN, ...(picked.size ? [DONE] : [])],
|
|
70
|
+
signal ? { signal } : undefined,
|
|
71
|
+
);
|
|
72
|
+
if (choice === undefined) return undefined;
|
|
73
|
+
if (choice === OWN) return own();
|
|
74
|
+
if (choice === DONE)
|
|
75
|
+
return { ...base, selected: [...picked].sort((a, b) => a - b).map((at) => question.options[at].label) };
|
|
76
|
+
const at = shown.indexOf(choice);
|
|
77
|
+
if (picked.has(at)) picked.delete(at);
|
|
78
|
+
else picked.add(at);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A yes-or-no question in the same dialog as ask_user, for Freeflow's own confirmations (task memory's proposals).
|
|
83
|
+
* Outside the terminal it is Pi's confirm, unchanged; without a UI it is false.
|
|
84
|
+
*/
|
|
85
|
+
export async function confirmWithUser(ctx, title, message, options = {}) {
|
|
86
|
+
if (!ctx?.hasUI || !ctx.ui) return false;
|
|
87
|
+
if (ctx.mode !== "tui" || typeof ctx.ui.custom !== "function")
|
|
88
|
+
return Boolean(
|
|
89
|
+
await ctx.ui.confirm(
|
|
90
|
+
title,
|
|
91
|
+
message,
|
|
92
|
+
...(options.timeoutMs !== undefined ? [{ timeout: options.timeoutMs }] : []),
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
const header = title.replace(/\?$/, "").slice(0, 40);
|
|
96
|
+
const yes = options.yes ?? "Yes";
|
|
97
|
+
const outcome = await askUser(
|
|
98
|
+
ctx,
|
|
99
|
+
[{ question: message, header, options: [{ label: yes }, { label: options.no ?? "No" }] }],
|
|
100
|
+
undefined,
|
|
101
|
+
{
|
|
102
|
+
ownAnswer: false,
|
|
103
|
+
notes: false,
|
|
104
|
+
...(options.timeoutMs !== undefined ? { deadline: Date.now() + options.timeoutMs } : {}),
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
return outcome.status === "answered" && outcome.answers[0]?.selected[0] === yes;
|
|
108
|
+
}
|