@aperant/framework 0.8.1 → 0.8.6
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 +443 -1
- package/agents/apt-planner.md +39 -7
- package/dist/cli/commands/adr.mjs +1 -1
- package/dist/cli/commands/audit-branch-current.d.mts +25 -0
- package/dist/cli/commands/audit-branch-current.d.mts.map +1 -0
- package/dist/cli/commands/audit-branch-current.mjs +252 -0
- package/dist/cli/commands/audit-branch-current.mjs.map +1 -0
- package/dist/cli/commands/audit.d.mts.map +1 -1
- package/dist/cli/commands/audit.mjs +13 -2
- package/dist/cli/commands/audit.mjs.map +1 -1
- package/dist/cli/commands/check-version.d.mts.map +1 -1
- package/dist/cli/commands/check-version.mjs +19 -1
- package/dist/cli/commands/check-version.mjs.map +1 -1
- package/dist/cli/commands/context.mjs +3 -3
- package/dist/cli/commands/context.mjs.map +1 -1
- package/dist/cli/commands/coverage-check.d.mts.map +1 -1
- package/dist/cli/commands/coverage-check.mjs +74 -7
- package/dist/cli/commands/coverage-check.mjs.map +1 -1
- package/dist/cli/commands/detect-runtime.d.mts +6 -0
- package/dist/cli/commands/detect-runtime.d.mts.map +1 -0
- package/dist/cli/commands/detect-runtime.mjs +142 -0
- package/dist/cli/commands/detect-runtime.mjs.map +1 -0
- package/dist/cli/commands/driver-doctor.d.mts +29 -0
- package/dist/cli/commands/driver-doctor.d.mts.map +1 -0
- package/dist/cli/commands/driver-doctor.mjs +291 -0
- package/dist/cli/commands/driver-doctor.mjs.map +1 -0
- package/dist/cli/commands/gate.mjs +14 -0
- package/dist/cli/commands/gate.mjs.map +1 -1
- package/dist/cli/commands/init.d.mts.map +1 -1
- package/dist/cli/commands/init.mjs +9 -6
- package/dist/cli/commands/init.mjs.map +1 -1
- package/dist/cli/commands/install-from-source.d.mts +2 -0
- package/dist/cli/commands/install-from-source.d.mts.map +1 -0
- package/dist/cli/commands/install-from-source.mjs +2 -0
- package/dist/cli/commands/install-from-source.mjs.map +1 -0
- package/dist/cli/commands/modes.mjs +1 -1
- package/dist/cli/commands/route.d.mts.map +1 -1
- package/dist/cli/commands/route.mjs +38 -1
- package/dist/cli/commands/route.mjs.map +1 -1
- package/dist/cli/commands/triage.mjs +1 -1
- package/dist/cli/config/load.d.mts.map +1 -1
- package/dist/cli/config/load.mjs +9 -3
- package/dist/cli/config/load.mjs.map +1 -1
- package/dist/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
- package/dist/cli/coordination/event-schema.d.mts.map +1 -1
- package/dist/cli/coordination/event-schema.mjs +18 -0
- package/dist/cli/coordination/event-schema.mjs.map +1 -1
- package/dist/cli/coverage-check/user-outcomes.d.mts +84 -0
- package/dist/cli/coverage-check/user-outcomes.d.mts.map +1 -0
- package/dist/cli/coverage-check/user-outcomes.mjs +265 -0
- package/dist/cli/coverage-check/user-outcomes.mjs.map +1 -0
- package/dist/cli/dispatch.d.mts.map +1 -1
- package/dist/cli/dispatch.mjs +18 -1
- package/dist/cli/dispatch.mjs.map +1 -1
- package/dist/cli/gate/context.d.mts +1 -0
- package/dist/cli/gate/context.d.mts.map +1 -1
- package/dist/cli/gate/context.mjs +14 -1
- package/dist/cli/gate/context.mjs.map +1 -1
- package/dist/cli/gate/gates/consistency-check.d.mts +1 -0
- package/dist/cli/gate/gates/consistency-check.d.mts.map +1 -1
- package/dist/cli/gate/gates/consistency-check.mjs +1 -0
- package/dist/cli/gate/gates/consistency-check.mjs.map +1 -1
- package/dist/cli/gate/gates/review-clean.d.mts +1 -0
- package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
- package/dist/cli/gate/gates/review-clean.mjs +1 -0
- package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
- package/dist/cli/gate/gates/verify-approved.d.mts +1 -0
- package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
- package/dist/cli/gate/gates/verify-approved.mjs +1 -0
- package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
- package/dist/cli/gate/registry.d.mts.map +1 -1
- package/dist/cli/gate/registry.mjs +9 -1
- package/dist/cli/gate/registry.mjs.map +1 -1
- package/dist/cli/host/detect.mjs +1 -1
- package/dist/cli/host/detect.mjs.map +1 -1
- package/dist/cli/install/find-workspace-framework-version.d.mts +16 -0
- package/dist/cli/install/find-workspace-framework-version.d.mts.map +1 -0
- package/dist/cli/install/find-workspace-framework-version.mjs +85 -0
- package/dist/cli/install/find-workspace-framework-version.mjs.map +1 -0
- package/dist/cli/install/install-from-source.d.mts +10 -0
- package/dist/cli/install/install-from-source.d.mts.map +1 -0
- package/dist/cli/install/install-from-source.mjs +271 -0
- package/dist/cli/install/install-from-source.mjs.map +1 -0
- package/dist/cli/install/install-kernel.d.mts.map +1 -1
- package/dist/cli/install/install-kernel.mjs +24 -0
- package/dist/cli/install/install-kernel.mjs.map +1 -1
- package/dist/cli/install/legacy-paths.d.mts +33 -11
- package/dist/cli/install/legacy-paths.d.mts.map +1 -1
- package/dist/cli/install/legacy-paths.mjs +163 -39
- package/dist/cli/install/legacy-paths.mjs.map +1 -1
- package/dist/cli/install/runtime-detect.mjs +1 -1
- package/dist/cli/install/runtime-detect.mjs.map +1 -1
- package/dist/cli/install/runtime-migrate.d.mts +34 -14
- package/dist/cli/install/runtime-migrate.d.mts.map +1 -1
- package/dist/cli/install/runtime-migrate.mjs +63 -30
- package/dist/cli/install/runtime-migrate.mjs.map +1 -1
- package/dist/cli/install/transforms/codex.d.mts.map +1 -1
- package/dist/cli/install/transforms/codex.mjs +28 -17
- package/dist/cli/install/transforms/codex.mjs.map +1 -1
- package/dist/cli/skill-author/contract.mjs +2 -2
- package/dist/cli/skill-author/contract.mjs.map +1 -1
- package/dist/cli/verify-proof/audit.d.mts +34 -0
- package/dist/cli/verify-proof/audit.d.mts.map +1 -0
- package/dist/cli/verify-proof/audit.mjs +53 -0
- package/dist/cli/verify-proof/audit.mjs.map +1 -0
- package/dist/cli/verify-proof/exec.d.mts +20 -0
- package/dist/cli/verify-proof/exec.d.mts.map +1 -0
- package/dist/cli/verify-proof/exec.mjs +74 -0
- package/dist/cli/verify-proof/exec.mjs.map +1 -0
- package/dist/cli/verify-proof/idl/index.d.mts +2 -0
- package/dist/cli/verify-proof/idl/index.d.mts.map +1 -0
- package/dist/cli/verify-proof/idl/index.mjs +13 -0
- package/dist/cli/verify-proof/idl/index.mjs.map +1 -0
- package/dist/cli/verify-proof/idl/types.d.ts +10 -0
- package/dist/cli/verify-proof/idl/types.d.ts.map +1 -0
- package/dist/cli/verify-proof/idl/types.js +10 -0
- package/dist/cli/verify-proof/idl/types.js.map +1 -0
- package/dist/cli/verify-proof/manifest-validator.d.mts +19 -0
- package/dist/cli/verify-proof/manifest-validator.d.mts.map +1 -0
- package/dist/cli/verify-proof/manifest-validator.mjs +182 -0
- package/dist/cli/verify-proof/manifest-validator.mjs.map +1 -0
- package/dist/cli/verify-proof/resolver.d.mts +105 -0
- package/dist/cli/verify-proof/resolver.d.mts.map +1 -0
- package/dist/cli/verify-proof/resolver.mjs +320 -0
- package/dist/cli/verify-proof/resolver.mjs.map +1 -0
- package/dist/cli/verify-proof/runtime-detect.d.mts +30 -0
- package/dist/cli/verify-proof/runtime-detect.d.mts.map +1 -0
- package/dist/cli/verify-proof/runtime-detect.mjs +209 -0
- package/dist/cli/verify-proof/runtime-detect.mjs.map +1 -0
- package/dist/cli/verify-proof/trust.d.mts +70 -0
- package/dist/cli/verify-proof/trust.d.mts.map +1 -0
- package/dist/cli/verify-proof/trust.mjs +174 -0
- package/dist/cli/verify-proof/trust.mjs.map +1 -0
- package/dist/driver-sdk/conformance.d.ts +41 -0
- package/dist/driver-sdk/conformance.d.ts.map +1 -0
- package/dist/driver-sdk/conformance.js +123 -0
- package/dist/driver-sdk/conformance.js.map +1 -0
- package/dist/driver-sdk/errors.d.ts +75 -0
- package/dist/driver-sdk/errors.d.ts.map +1 -0
- package/dist/driver-sdk/errors.js +80 -0
- package/dist/driver-sdk/errors.js.map +1 -0
- package/dist/driver-sdk/idl.d.ts +221 -0
- package/dist/driver-sdk/idl.d.ts.map +1 -0
- package/dist/driver-sdk/idl.js +140 -0
- package/dist/driver-sdk/idl.js.map +1 -0
- package/dist/driver-sdk/index.d.ts +28 -0
- package/dist/driver-sdk/index.d.ts.map +1 -0
- package/dist/driver-sdk/index.js +28 -0
- package/dist/driver-sdk/index.js.map +1 -0
- package/dist/driver-sdk/manifest.d.ts +93 -0
- package/dist/driver-sdk/manifest.d.ts.map +1 -0
- package/dist/driver-sdk/manifest.js +12 -0
- package/dist/driver-sdk/manifest.js.map +1 -0
- package/dist/driver-sdk/retry.d.ts +33 -0
- package/dist/driver-sdk/retry.d.ts.map +1 -0
- package/dist/driver-sdk/retry.js +50 -0
- package/dist/driver-sdk/retry.js.map +1 -0
- package/dist/plugin/.claude-plugin/plugin.json +4 -1
- package/dist/plugin/agents/apt-planner.md +39 -7
- package/dist/plugin/skills/apt/SKILL.md +75 -6
- package/dist/plugin/skills/apt-caveman/SKILL.md +64 -0
- package/dist/plugin/skills/apt-debug/SKILL.md +36 -4
- package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
- package/dist/plugin/skills/apt-discuss/SKILL.md +4 -4
- package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +2 -11
- package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
- package/dist/plugin/skills/apt-execute/SKILL.md +27 -4
- package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +2 -10
- package/dist/plugin/skills/apt-fan-out/SKILL.md +36 -1
- package/dist/plugin/skills/apt-handoff/SKILL.md +228 -0
- package/dist/plugin/skills/apt-improve/DEEPENING.md +0 -8
- package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
- package/dist/plugin/skills/apt-improve/LANGUAGE.md +0 -8
- package/dist/plugin/skills/apt-improve/SKILL.md +4 -4
- package/dist/plugin/skills/apt-plan/SKILL.md +73 -5
- package/dist/plugin/skills/apt-plan/adapters/conductor.md +1 -1
- package/dist/plugin/skills/apt-pr-review/SKILL.md +1 -1
- package/dist/plugin/skills/apt-prototype/LOGIC.md +0 -8
- package/dist/plugin/skills/apt-prototype/SKILL.md +4 -4
- package/dist/plugin/skills/apt-prototype/UI.md +0 -8
- package/dist/plugin/skills/apt-quick/SKILL.md +31 -1
- package/dist/plugin/skills/apt-research/SKILL.md +526 -0
- package/dist/plugin/skills/apt-research/appendices/budget-loop.md +397 -0
- package/dist/plugin/skills/apt-research/appendices/claim-graph-schema.md +206 -0
- package/dist/plugin/skills/apt-research/appendices/domain-tools.md +236 -0
- package/dist/plugin/skills/apt-resume/SKILL.md +54 -31
- package/dist/plugin/skills/apt-run/SKILL.md +114 -16
- package/dist/plugin/skills/apt-setup/SKILL.md +97 -8
- package/dist/plugin/skills/apt-ship/SKILL.md +165 -9
- package/dist/plugin/skills/apt-spar/SKILL.md +84 -23
- package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +0 -8
- package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
- package/dist/plugin/skills/apt-triage/SKILL.md +2 -2
- package/dist/plugin/skills/apt-update/SKILL.md +74 -17
- package/dist/plugin/skills/apt-verify-proof/SKILL.md +87 -1
- package/dist/plugin/skills/apt-zoom-out/SKILL.md +3 -3
- package/dist/schemas/quick-task.d.ts +17 -17
- package/dist/schemas/quick-task.d.ts.map +1 -1
- package/dist/schemas/quick-task.js +2 -2
- package/dist/schemas/quick-task.js.map +1 -1
- package/dist/types/config.d.ts +33 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/state.d.ts +1 -1
- package/dist/types/state.d.ts.map +1 -1
- package/dist/types/task-record.d.ts +1 -1
- package/dist/types/task-record.d.ts.map +1 -1
- package/drivers/.gitkeep +0 -0
- package/drivers/api/README.md +40 -0
- package/drivers/api/driver.mjs +59 -0
- package/drivers/api/manifest.json +26 -0
- package/drivers/browser/README.md +105 -0
- package/drivers/browser/driver.mjs +134 -0
- package/drivers/browser/manifest.json +35 -0
- package/drivers/cli/README.md +44 -0
- package/drivers/cli/driver.mjs +62 -0
- package/drivers/cli/manifest.json +28 -0
- package/drivers/electron/README.md +64 -0
- package/drivers/electron/driver.mjs +87 -0
- package/drivers/electron/manifest.json +37 -0
- package/package.json +138 -133
- package/prompts/planner.md +1 -1
- package/prompts/spec_writer.md +1 -1
- package/skills/apt/SKILL.md +75 -6
- package/skills/apt-caveman/SKILL.md +64 -0
- package/skills/apt-debug/SKILL.md +36 -4
- package/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
- package/skills/apt-discuss/SKILL.md +4 -4
- package/skills/apt-discuss/appendices/grill-discipline.md +2 -11
- package/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
- package/skills/apt-execute/SKILL.md +27 -4
- package/skills/apt-execute/appendices/tdd-mode.md +2 -10
- package/skills/apt-fan-out/SKILL.md +36 -1
- package/skills/apt-handoff/SKILL.md +228 -0
- package/skills/apt-improve/DEEPENING.md +0 -8
- package/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
- package/skills/apt-improve/LANGUAGE.md +0 -8
- package/skills/apt-improve/SKILL.md +4 -4
- package/skills/apt-plan/SKILL.md +73 -5
- package/skills/apt-plan/adapters/conductor.md +1 -1
- package/skills/apt-planner.md +17 -1
- package/skills/apt-pr-review/SKILL.md +1 -1
- package/skills/apt-prototype/LOGIC.md +0 -8
- package/skills/apt-prototype/SKILL.md +4 -4
- package/skills/apt-prototype/UI.md +0 -8
- package/skills/apt-quick/SKILL.md +31 -1
- package/skills/apt-research/SKILL.md +526 -0
- package/skills/apt-research/appendices/budget-loop.md +397 -0
- package/skills/apt-research/appendices/claim-graph-schema.md +206 -0
- package/skills/apt-research/appendices/domain-tools.md +236 -0
- package/skills/apt-resume/SKILL.md +54 -31
- package/skills/apt-run/SKILL.md +114 -16
- package/skills/apt-setup/SKILL.md +97 -8
- package/skills/apt-ship/SKILL.md +165 -9
- package/skills/apt-spar/SKILL.md +84 -23
- package/skills/apt-triage/AGENT-BRIEF.md +0 -8
- package/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
- package/skills/apt-triage/SKILL.md +2 -2
- package/skills/apt-update/SKILL.md +74 -17
- package/skills/apt-verify-proof/SKILL.md +87 -1
- package/skills/apt-zoom-out/SKILL.md +3 -3
- package/src/cli/commands/adr.mjs +1 -1
- package/src/cli/commands/audit-branch-current.mjs +250 -0
- package/src/cli/commands/audit.mjs +13 -2
- package/src/cli/commands/check-version.mjs +20 -1
- package/src/cli/commands/context.mjs +3 -3
- package/src/cli/commands/coverage-check.mjs +126 -44
- package/src/cli/commands/detect-runtime.mjs +143 -0
- package/src/cli/commands/driver-doctor.mjs +294 -0
- package/src/cli/commands/gate.mjs +15 -0
- package/src/cli/commands/init.mjs +9 -6
- package/src/cli/commands/install-from-source.mjs +1 -0
- package/src/cli/commands/modes.mjs +1 -1
- package/src/cli/commands/route.mjs +42 -1
- package/src/cli/commands/triage.mjs +1 -1
- package/src/cli/config/load.mjs +9 -3
- package/src/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
- package/src/cli/coordination/event-schema.mjs +19 -0
- package/src/cli/coverage-check/user-outcomes.mjs +273 -0
- package/src/cli/dispatch.mjs +17 -1
- package/src/cli/gate/context.mjs +20 -1
- package/src/cli/gate/gates/consistency-check.mjs +1 -0
- package/src/cli/gate/gates/review-clean.mjs +1 -0
- package/src/cli/gate/gates/verify-approved.mjs +1 -0
- package/src/cli/gate/registry.mjs +11 -1
- package/src/cli/host/detect.mjs +1 -1
- package/src/cli/install/find-workspace-framework-version.mjs +88 -0
- package/src/cli/install/install-from-source.mjs +276 -0
- package/src/cli/install/install-kernel.mjs +24 -0
- package/src/cli/install/legacy-paths.mjs +164 -39
- package/src/cli/install/runtime-detect.mjs +1 -1
- package/src/cli/install/runtime-migrate.mjs +63 -30
- package/src/cli/install/transforms/codex.mjs +26 -17
- package/src/cli/skill-author/contract.mjs +2 -2
- package/src/cli/verify-proof/.gitkeep +0 -0
- package/src/cli/verify-proof/audit.mjs +55 -0
- package/src/cli/verify-proof/exec.mjs +74 -0
- package/src/cli/verify-proof/idl/index.mjs +23 -0
- package/src/cli/verify-proof/idl/types.ts +42 -0
- package/src/cli/verify-proof/manifest-schema.json +212 -0
- package/src/cli/verify-proof/manifest-validator.mjs +184 -0
- package/src/cli/verify-proof/resolver.mjs +330 -0
- package/src/cli/verify-proof/runtime-detect.mjs +211 -0
- package/src/cli/verify-proof/trust.mjs +187 -0
- package/templates/adr-format.md +0 -8
- package/templates/aperant-claude-md-appendix.md +1 -1
- package/templates/context-format.md +1 -9
- package/templates/proof-verification.md +51 -8
- package/workflows/verify-proof.md +130 -19
|
@@ -1250,12 +1250,15 @@ export async function cmdInit(projectDir, extraArgs = []) {
|
|
|
1250
1250
|
}
|
|
1251
1251
|
}
|
|
1252
1252
|
|
|
1253
|
-
// FRAMEWORK-BUG-021 — sweep the legacy `.
|
|
1253
|
+
// FRAMEWORK-BUG-021 — sweep the legacy `.codex/` codex tree
|
|
1254
1254
|
// BEFORE the install pipeline runs so the install can re-stamp
|
|
1255
|
-
// `.
|
|
1256
|
-
//
|
|
1257
|
-
//
|
|
1258
|
-
//
|
|
1255
|
+
// `.agents/skills/<folder>/SKILL.md` (Codex CLI's documented
|
|
1256
|
+
// surface, per https://developers.openai.com/codex/skills) against
|
|
1257
|
+
// a clean slate. Only fires when codex is in the requested runtime
|
|
1258
|
+
// set and `--keep-legacy` was not passed; the helper itself is
|
|
1259
|
+
// conservative (no-op on managed installs, no-op when no legacy
|
|
1260
|
+
// framework files are present). The v0.8.2 inversion is
|
|
1261
|
+
// documented in `runtime-migrate.mjs:7-12`.
|
|
1259
1262
|
let legacyCleanup = null
|
|
1260
1263
|
if (effectiveAnswers.runtimes.includes('codex')) {
|
|
1261
1264
|
if (parsedFlags.keepLegacy) {
|
|
@@ -1265,7 +1268,7 @@ export async function cmdInit(projectDir, extraArgs = []) {
|
|
|
1265
1268
|
legacyCleanup = { runtime: 'codex', ...cleanup }
|
|
1266
1269
|
if (cleanup.ran && cleanup.retained.length > 0) {
|
|
1267
1270
|
process.stderr.write(
|
|
1268
|
-
`[apt-tools] legacy_cleanup retained ${cleanup.retained.length} user-added path(s) under .
|
|
1271
|
+
`[apt-tools] legacy_cleanup retained ${cleanup.retained.length} user-added path(s) under .codex/: ${cleanup.retained.join(', ')}\n`,
|
|
1269
1272
|
)
|
|
1270
1273
|
}
|
|
1271
1274
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { cmdInstallFromSource } from '../install/install-from-source.mjs'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* commands/modes.mjs — `apt-tools modes list
|
|
2
|
+
* commands/modes.mjs — `apt-tools modes list`.
|
|
3
3
|
*
|
|
4
4
|
* Power-user discoverability tool. Returns a JSON envelope with one
|
|
5
5
|
* record per discovered skill, exposing modes / flags / content-formats /
|
|
@@ -28,7 +28,11 @@ import { existsSync, readFileSync } from 'node:fs'
|
|
|
28
28
|
import { homedir } from 'node:os'
|
|
29
29
|
import { dirname, join, resolve } from 'node:path'
|
|
30
30
|
import { fileURLToPath } from 'node:url'
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
getTaskIsolationConfig,
|
|
33
|
+
getTrustedSkillSources,
|
|
34
|
+
loadMergedProjectConfig,
|
|
35
|
+
} from '../config/load.mjs'
|
|
32
36
|
import '../gate/gates/index.mjs'
|
|
33
37
|
import * as gateRegistry from '../gate/registry.mjs'
|
|
34
38
|
import { detectHost, SUPPORTED_HOSTS } from '../host/detect.mjs'
|
|
@@ -264,6 +268,19 @@ export function cmdRoute(projectDir, extraArgs) {
|
|
|
264
268
|
else if (f === 'debug') track_override = 'DEBUG'
|
|
265
269
|
}
|
|
266
270
|
|
|
271
|
+
// FRAMEWORK-RFC-001 — consent signal for spar pipeline auto-inject.
|
|
272
|
+
// Reads merged config (config.json ∪ config.local.json) via the same
|
|
273
|
+
// helper apt-setup writes through. AUDIT-001 — spar fields live in
|
|
274
|
+
// .aperant/config.local.json, not shared config. Resolved once and
|
|
275
|
+
// folded into the envelope by both classify branches below.
|
|
276
|
+
// `loadMergedProjectConfig` returns null when no shared config exists
|
|
277
|
+
// (first-time init path) — treat as consent-off + no partner.
|
|
278
|
+
const merged = loadMergedProjectConfig(targetDir) ?? {}
|
|
279
|
+
const sparEnabled = merged.spar?.enabled === true
|
|
280
|
+
const sparPartner = merged.spar?.partner ?? null
|
|
281
|
+
const userPassedExplicitSparGates =
|
|
282
|
+
rawInput.includes('--spar-gates') || flags.some((f) => f.startsWith('spar-gates'))
|
|
283
|
+
|
|
267
284
|
const taskDescription = normalizeAptInvocation(rawInput.replace(/--\w+/g, '').trim())
|
|
268
285
|
|
|
269
286
|
const configPath = join(targetDir, '.aperant', 'config.json')
|
|
@@ -399,6 +416,17 @@ export function cmdRoute(projectDir, extraArgs) {
|
|
|
399
416
|
const agentName = spawnAgent ? index.agentOf(primarySkill) || 'apt-planner' : null
|
|
400
417
|
const options = buildTrackOptions(track)
|
|
401
418
|
const gate_preview = previewGatesFor(primarySkill)
|
|
419
|
+
// FRAMEWORK-RFC-001 — flag-override path: track is known deterministically,
|
|
420
|
+
// so we can resolve the spar gate string inline and emit it directly.
|
|
421
|
+
// QUICK/DEBUG are Fast-Path-exempt — no inject. User-passed --spar-gates
|
|
422
|
+
// wins over consent (no double-inject). Field omitted entirely when null
|
|
423
|
+
// so the envelope stays byte-equivalent for users who never opted in.
|
|
424
|
+
let spar_inject_gates = null
|
|
425
|
+
if (sparEnabled && !userPassedExplicitSparGates) {
|
|
426
|
+
if (track === 'STANDARD') spar_inject_gates = 'plan'
|
|
427
|
+
else if (track === 'DEEP') spar_inject_gates = 'plan,execute'
|
|
428
|
+
// QUICK / DEBUG → stays null
|
|
429
|
+
}
|
|
402
430
|
return ok({
|
|
403
431
|
status: 'ok',
|
|
404
432
|
command: 'route',
|
|
@@ -417,6 +445,7 @@ export function cmdRoute(projectDir, extraArgs) {
|
|
|
417
445
|
task_isolation,
|
|
418
446
|
update_check,
|
|
419
447
|
...(gate_preview ? { gate_preview } : {}),
|
|
448
|
+
...(spar_inject_gates ? { spar_inject_gates } : {}),
|
|
420
449
|
classification: {
|
|
421
450
|
source: 'flag-override',
|
|
422
451
|
reason: `Explicit --${track_override.toLowerCase()} flag`,
|
|
@@ -467,6 +496,18 @@ export function cmdRoute(projectDir, extraArgs) {
|
|
|
467
496
|
reason:
|
|
468
497
|
'Track and skill classification deferred to the host LLM; see classification_meta.trust_notice.',
|
|
469
498
|
},
|
|
499
|
+
// FRAMEWORK-RFC-001 — host model composes --spar-gates from these
|
|
500
|
+
// signals + its track classification per apt/SKILL.md MODE: classify
|
|
501
|
+
// Step 4. Field is absent when consent is off (envelope stays slim).
|
|
502
|
+
...(sparEnabled
|
|
503
|
+
? {
|
|
504
|
+
spar_consent: {
|
|
505
|
+
enabled: true,
|
|
506
|
+
partner: sparPartner,
|
|
507
|
+
user_passed_explicit_gates: userPassedExplicitSparGates,
|
|
508
|
+
},
|
|
509
|
+
}
|
|
510
|
+
: {}),
|
|
470
511
|
},
|
|
471
512
|
})
|
|
472
513
|
} catch (e) {
|
package/src/cli/config/load.mjs
CHANGED
|
@@ -361,11 +361,17 @@ export function getTrustedSkillSources(projectDir, cli) {
|
|
|
361
361
|
)
|
|
362
362
|
if (existsSync(consumerSkills)) defaults.push(consumerSkills)
|
|
363
363
|
// 4. Host CLI's installed runtime skills — only when the router knows
|
|
364
|
-
// which runtime it's in. Some hosts (
|
|
365
|
-
// .
|
|
364
|
+
// which runtime it's in. Some hosts (claude-code, gemini, …) install into
|
|
365
|
+
// .claude/skills / .gemini/skills etc; the router picks skills from
|
|
366
366
|
// the host-local directory so per-runtime transforms are honored.
|
|
367
|
+
//
|
|
368
|
+
// Codex is excluded: its transform strips frontmatter to {name, description}
|
|
369
|
+
// for codex validator compliance, and that stripped form fails the
|
|
370
|
+
// router's SkillFrontmatterSchema (contract.mjs). The canonical + kernel
|
|
371
|
+
// roots above still serve routing on Codex hosts (they carry the fat
|
|
372
|
+
// frontmatter the schema requires).
|
|
367
373
|
const installRoot = cli ? cliToInstallRoot(cli) : null
|
|
368
|
-
if (installRoot) {
|
|
374
|
+
if (installRoot && cli !== 'codex') {
|
|
369
375
|
const runtimeSkills = join(targetDir, installRoot, 'skills')
|
|
370
376
|
if (existsSync(runtimeSkills)) defaults.push(runtimeSkills)
|
|
371
377
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* postcondition of `apt-tools commit`. This closes the Conductor v2 gap
|
|
8
8
|
* exposed during live verification 2026-05-15: the planner's QUICK /
|
|
9
9
|
* SIMPLE track is hard-exempt from "new interaction discipline"
|
|
10
|
-
* (
|
|
10
|
+
* (Aperant Fast Path Guarantee ID-05), so the markdown emit step was
|
|
11
11
|
* never executed for tasks router-classified as simple. The fleet
|
|
12
12
|
* never saw artifact.ready, the PlanReadyCard never auto-promoted.
|
|
13
13
|
*
|
|
@@ -42,6 +42,10 @@ const CONDUCTOR_OPS = new Set([
|
|
|
42
42
|
'conductor.action.executed',
|
|
43
43
|
'conductor.action.gate_blocked',
|
|
44
44
|
'conductor.boot_reconciled',
|
|
45
|
+
// AC21 — MCP-server consent decisions audited to .aperant/events/*.jsonl
|
|
46
|
+
'mcp-consent.approved',
|
|
47
|
+
'mcp-consent.denied',
|
|
48
|
+
'mcp-consent.tofu-reprompt',
|
|
45
49
|
])
|
|
46
50
|
|
|
47
51
|
/** Allowed values for `phase.changed.to` — PTY busy/idle only. */
|
|
@@ -133,6 +137,21 @@ export function validateConductorEvent(op, data) {
|
|
|
133
137
|
'blocked_reason',
|
|
134
138
|
])
|
|
135
139
|
|
|
140
|
+
// AC21 — MCP server consent audit. Three ops share the same
|
|
141
|
+
// payload shape: who approved/denied which server, with which
|
|
142
|
+
// configHash, at what time. session_id is a session-scoped string
|
|
143
|
+
// (chat-session row id).
|
|
144
|
+
case 'mcp-consent.approved':
|
|
145
|
+
case 'mcp-consent.denied':
|
|
146
|
+
case 'mcp-consent.tofu-reprompt':
|
|
147
|
+
return requireFields(op, d, [
|
|
148
|
+
'server_id',
|
|
149
|
+
'config_sha256',
|
|
150
|
+
'decided_at',
|
|
151
|
+
'decided_by',
|
|
152
|
+
'session_id',
|
|
153
|
+
])
|
|
154
|
+
|
|
136
155
|
case 'conductor.boot_reconciled': {
|
|
137
156
|
// Fleet-wide event — numeric fields only. All optional but if
|
|
138
157
|
// present must be finite numbers (zero allowed).
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coverage-check/user-outcomes.mjs — `## User Outcomes` PRD section parser.
|
|
3
|
+
*
|
|
4
|
+
* Pure, file-IO-free. Mirrors the `coverage-check/acceptance-criteria.mjs`
|
|
5
|
+
* pattern: takes spec.md content as a string and returns a structured
|
|
6
|
+
* `{header_present, body_kind, epic?, outcomes[], validation_warnings?}`
|
|
7
|
+
* envelope the coverage-check command consumes.
|
|
8
|
+
*
|
|
9
|
+
* The `## User Outcomes` section is the verify-proof input contract
|
|
10
|
+
* introduced in v0.8.6 (spec.md §ID-01 / §ID-07). The grammar:
|
|
11
|
+
*
|
|
12
|
+
* ## User Outcomes
|
|
13
|
+
*
|
|
14
|
+
* **Epic:** {one-sentence headline}
|
|
15
|
+
*
|
|
16
|
+
* - **ON** [surface]: {prose}
|
|
17
|
+
* - **ON** [surface]: {prose} *(requires ON N)*
|
|
18
|
+
*
|
|
19
|
+
* (refactor-only escape — INSTEAD of the outcome list:)
|
|
20
|
+
* _No user-observable changes — pure refactor._
|
|
21
|
+
*
|
|
22
|
+
* `surface` is the closed set: electron | web | cli | api | electron+web.
|
|
23
|
+
*
|
|
24
|
+
* **Positional ID synthesis (§ID-07 / M1).** The parser counts outcome
|
|
25
|
+
* rows in document order and synthesizes `id = "O" + (i+1)`. The literal
|
|
26
|
+
* `**ON**` regex stays — IDs are NOT author-written. The `requires N`
|
|
27
|
+
* digit captured from `*(requires ON N)*` is validated against the set
|
|
28
|
+
* of synthesized IDs; on mismatch the parser emits a
|
|
29
|
+
* `validation_warnings[]` entry of kind `unknown_requires_target`,
|
|
30
|
+
* skips the dep edge, but still includes the outcome row.
|
|
31
|
+
*
|
|
32
|
+
* See packages/framework/skills/apt-plan/SKILL.md Section 5.B for the
|
|
33
|
+
* authoring contract.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const VALID_SURFACES = new Set(['electron', 'web', 'cli', 'api', 'electron+web'])
|
|
37
|
+
|
|
38
|
+
const HEADING_RE = /^(#{1,6})\s+(.*)$/
|
|
39
|
+
const USER_OUTCOMES_HEADING_RE = /^user\s+outcomes\s*$/i
|
|
40
|
+
const EPIC_RE = /^\*\*Epic:\*\*\s+(.+)$/
|
|
41
|
+
const EMPTY_WITH_NOTE_RE = /^_No user-observable changes — pure refactor\._\s*$/
|
|
42
|
+
const OUTCOME_RE =
|
|
43
|
+
/^-\s+\*\*ON\*\*\s+\[(electron|web|cli|api|electron\+web)\]:\s+(.+?)(\s+\*\(requires\s+ON\s+(\d+)\)\*)?\s*$/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {Object} UserOutcome
|
|
47
|
+
* @property {string} id — synthesized positional ID (O1, O2, ...)
|
|
48
|
+
* @property {('electron'|'web'|'cli'|'api'|'electron+web')} surface
|
|
49
|
+
* @property {string} text — outcome prose (sans the surface tag and requires suffix)
|
|
50
|
+
* @property {number} [requires] — the digit captured from `*(requires ON N)*`, when present and valid
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {Object} ValidationWarning
|
|
55
|
+
* @property {'unknown_requires_target'} kind
|
|
56
|
+
* @property {string} outcome_id — synthesized ID of the outcome carrying the bad `requires`
|
|
57
|
+
* @property {number} requires — the offending digit
|
|
58
|
+
* @property {string} reason — human-readable explanation
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @typedef {Object} UserOutcomesParseResult
|
|
63
|
+
* @property {boolean} header_present
|
|
64
|
+
* @property {('absent'|'empty'|'empty-with-note'|'outcomes')} body_kind
|
|
65
|
+
* @property {string} [epic]
|
|
66
|
+
* @property {UserOutcome[]} outcomes
|
|
67
|
+
* @property {ValidationWarning[]} [validation_warnings]
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parse the `## User Outcomes` section from a spec.md string.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} specContent
|
|
74
|
+
* @returns {UserOutcomesParseResult}
|
|
75
|
+
*/
|
|
76
|
+
export function parseUserOutcomes(specContent) {
|
|
77
|
+
if (typeof specContent !== 'string' || specContent.length === 0) {
|
|
78
|
+
return { header_present: false, body_kind: 'absent', outcomes: [] }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const lines = specContent.split('\n')
|
|
82
|
+
|
|
83
|
+
// Find the `## User Outcomes` heading + scope to its body (until the next
|
|
84
|
+
// heading of equal or lower depth).
|
|
85
|
+
let headingLine = -1
|
|
86
|
+
let headingLevel = 0
|
|
87
|
+
for (let i = 0; i < lines.length; i++) {
|
|
88
|
+
const m = lines[i].trim().match(HEADING_RE)
|
|
89
|
+
if (m && USER_OUTCOMES_HEADING_RE.test(m[2])) {
|
|
90
|
+
headingLine = i
|
|
91
|
+
headingLevel = m[1].length
|
|
92
|
+
break
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (headingLine === -1) {
|
|
97
|
+
return { header_present: false, body_kind: 'absent', outcomes: [] }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Collect body lines from heading+1 until the next heading of ANY
|
|
101
|
+
// depth. The User Outcomes section is intentionally flat — a
|
|
102
|
+
// downstream sub-heading (e.g. `### Acceptance Criteria` following
|
|
103
|
+
// directly without a `## ` separator) ends the body. This matches
|
|
104
|
+
// how authors write the section and avoids accidentally pulling
|
|
105
|
+
// stray prose from later sections into the outcomes pool.
|
|
106
|
+
const bodyLines = []
|
|
107
|
+
for (let i = headingLine + 1; i < lines.length; i++) {
|
|
108
|
+
const trimmed = lines[i].trim()
|
|
109
|
+
if (HEADING_RE.test(trimmed)) break
|
|
110
|
+
bodyLines.push(lines[i])
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Classify body shape.
|
|
114
|
+
const nonBlank = bodyLines.map((l) => l.trim()).filter((l) => l.length > 0)
|
|
115
|
+
|
|
116
|
+
if (nonBlank.length === 0) {
|
|
117
|
+
return { header_present: true, body_kind: 'empty', outcomes: [] }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Empty-with-note escape — body MUST contain only the verbatim
|
|
121
|
+
// italic-underscore-em-dash-period sentinel (other lines may exist
|
|
122
|
+
// only if they are the Epic header or blank — but the spec says the
|
|
123
|
+
// sentinel REPLACES the outcome list, so we require it to be the only
|
|
124
|
+
// non-blank line OR the only non-Epic non-blank line).
|
|
125
|
+
let epic
|
|
126
|
+
const sentinelLines = []
|
|
127
|
+
const outcomeLines = []
|
|
128
|
+
for (const line of nonBlank) {
|
|
129
|
+
const epicMatch = line.match(EPIC_RE)
|
|
130
|
+
if (epicMatch) {
|
|
131
|
+
epic = epicMatch[1].trim()
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
if (EMPTY_WITH_NOTE_RE.test(line)) {
|
|
135
|
+
sentinelLines.push(line)
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
outcomeLines.push(line)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// If the sentinel is present and there are NO outcome lines, this is
|
|
142
|
+
// the empty-with-note branch. The sentinel may co-exist with an Epic
|
|
143
|
+
// header — that's not required but not forbidden either.
|
|
144
|
+
if (sentinelLines.length > 0 && outcomeLines.length === 0) {
|
|
145
|
+
/** @type {UserOutcomesParseResult} */
|
|
146
|
+
const result = {
|
|
147
|
+
header_present: true,
|
|
148
|
+
body_kind: 'empty-with-note',
|
|
149
|
+
outcomes: [],
|
|
150
|
+
}
|
|
151
|
+
if (epic) result.epic = epic
|
|
152
|
+
return result
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// If neither the sentinel nor any outcome rows are present, the body
|
|
156
|
+
// is effectively empty (header present, no parseable content).
|
|
157
|
+
if (outcomeLines.length === 0) {
|
|
158
|
+
/** @type {UserOutcomesParseResult} */
|
|
159
|
+
const result = {
|
|
160
|
+
header_present: true,
|
|
161
|
+
body_kind: 'empty',
|
|
162
|
+
outcomes: [],
|
|
163
|
+
}
|
|
164
|
+
if (epic) result.epic = epic
|
|
165
|
+
return result
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Parse outcome rows in document order. Skip non-matching lines (e.g.
|
|
169
|
+
// stray prose between outcomes) — they don't count as outcomes.
|
|
170
|
+
/** @type {UserOutcome[]} */
|
|
171
|
+
const outcomes = []
|
|
172
|
+
const rawRequires = []
|
|
173
|
+
for (const line of outcomeLines) {
|
|
174
|
+
const m = line.match(OUTCOME_RE)
|
|
175
|
+
if (!m) continue
|
|
176
|
+
const surface = /** @type {UserOutcome['surface']} */ (m[1])
|
|
177
|
+
if (!VALID_SURFACES.has(surface)) continue
|
|
178
|
+
const text = m[2].trim()
|
|
179
|
+
const id = 'O' + (outcomes.length + 1)
|
|
180
|
+
const outcome = /** @type {UserOutcome} */ ({ id, surface, text })
|
|
181
|
+
if (m[4]) {
|
|
182
|
+
const n = Number.parseInt(m[4], 10)
|
|
183
|
+
rawRequires.push({ outcomeIndex: outcomes.length, requires: n })
|
|
184
|
+
}
|
|
185
|
+
outcomes.push(outcome)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (outcomes.length === 0) {
|
|
189
|
+
// Header present, prose present, but no parseable `**ON**` rows —
|
|
190
|
+
// effectively empty.
|
|
191
|
+
/** @type {UserOutcomesParseResult} */
|
|
192
|
+
const result = {
|
|
193
|
+
header_present: true,
|
|
194
|
+
body_kind: 'empty',
|
|
195
|
+
outcomes: [],
|
|
196
|
+
}
|
|
197
|
+
if (epic) result.epic = epic
|
|
198
|
+
return result
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Validate `requires` cross-refs against the set of synthesized IDs.
|
|
202
|
+
/** @type {ValidationWarning[]} */
|
|
203
|
+
const warnings = []
|
|
204
|
+
for (const r of rawRequires) {
|
|
205
|
+
if (r.requires >= 1 && r.requires <= outcomes.length) {
|
|
206
|
+
outcomes[r.outcomeIndex].requires = r.requires
|
|
207
|
+
} else {
|
|
208
|
+
warnings.push({
|
|
209
|
+
kind: 'unknown_requires_target',
|
|
210
|
+
outcome_id: outcomes[r.outcomeIndex].id,
|
|
211
|
+
requires: r.requires,
|
|
212
|
+
reason: `requires ON ${r.requires} but only ${outcomes.length} outcome(s) parsed`,
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** @type {UserOutcomesParseResult} */
|
|
218
|
+
const result = {
|
|
219
|
+
header_present: true,
|
|
220
|
+
body_kind: 'outcomes',
|
|
221
|
+
outcomes,
|
|
222
|
+
}
|
|
223
|
+
if (epic) result.epic = epic
|
|
224
|
+
if (warnings.length > 0) result.validation_warnings = warnings
|
|
225
|
+
return result
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Compute the coverage-check gate result for a parsed user-outcomes
|
|
230
|
+
* envelope. Per §ID-07 table — warn-not-block in v0.8.x, hard-block in
|
|
231
|
+
* v0.9.0.
|
|
232
|
+
*
|
|
233
|
+
* | header_present | body_kind | v0.8.x | v0.9.0 |
|
|
234
|
+
* |----------------|-------------------|--------|--------|
|
|
235
|
+
* | false | — | warn | block |
|
|
236
|
+
* | true | empty-with-note | pass | pass |
|
|
237
|
+
* | true | outcomes (≥1) | pass | pass |
|
|
238
|
+
* | true | empty | block | block |
|
|
239
|
+
*
|
|
240
|
+
* @param {UserOutcomesParseResult} parsed
|
|
241
|
+
* @param {string} frameworkVersion — semver string from the kernel's package.json (e.g. "0.8.6")
|
|
242
|
+
* @returns {('pass'|'warn'|'block')}
|
|
243
|
+
*/
|
|
244
|
+
export function userOutcomesGateResult(parsed, frameworkVersion) {
|
|
245
|
+
if (parsed.body_kind === 'empty-with-note' || parsed.body_kind === 'outcomes') {
|
|
246
|
+
return 'pass'
|
|
247
|
+
}
|
|
248
|
+
if (parsed.body_kind === 'empty') {
|
|
249
|
+
// Header present, body empty — author started the section and forgot
|
|
250
|
+
// to fill it in. Block at every version.
|
|
251
|
+
return 'block'
|
|
252
|
+
}
|
|
253
|
+
// Missing header (body_kind: 'absent'). Warn in 0.8.x, block in 0.9.0+.
|
|
254
|
+
const major = parseFrameworkMajorMinor(frameworkVersion)
|
|
255
|
+
if (major === null) return 'warn'
|
|
256
|
+
const { major: maj, minor: min } = major
|
|
257
|
+
if (maj > 0 || (maj === 0 && min >= 9)) return 'block'
|
|
258
|
+
return 'warn'
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Parse the leading `major.minor` of a semver string. Returns null on
|
|
263
|
+
* malformed input (caller treats as "warn" — fail-soft).
|
|
264
|
+
*
|
|
265
|
+
* @param {string} version
|
|
266
|
+
* @returns {{major: number, minor: number} | null}
|
|
267
|
+
*/
|
|
268
|
+
function parseFrameworkMajorMinor(version) {
|
|
269
|
+
if (typeof version !== 'string') return null
|
|
270
|
+
const m = version.match(/^(\d+)\.(\d+)/)
|
|
271
|
+
if (!m) return null
|
|
272
|
+
return { major: Number.parseInt(m[1], 10), minor: Number.parseInt(m[2], 10) }
|
|
273
|
+
}
|
package/src/cli/dispatch.mjs
CHANGED
|
@@ -53,7 +53,9 @@ import { cmdDesignScan } from './commands/design-scan.mjs'
|
|
|
53
53
|
import { cmdDesignTemplate } from './commands/design-template.mjs'
|
|
54
54
|
import { cmdDesignValidate } from './commands/design-validate.mjs'
|
|
55
55
|
import { cmdDetectProject } from './commands/detect-project.mjs'
|
|
56
|
+
import { cmdDetectRuntime } from './commands/detect-runtime.mjs'
|
|
56
57
|
import { cmdDocsAudit } from './commands/docs-audit.mjs'
|
|
58
|
+
import { cmdDriverDoctor } from './commands/driver-doctor.mjs'
|
|
57
59
|
import { cmdEditCheck } from './commands/edit-check.mjs'
|
|
58
60
|
import { cmdEffects } from './commands/effects.mjs'
|
|
59
61
|
import { cmdEvent } from './commands/event.mjs'
|
|
@@ -64,6 +66,7 @@ import { cmdHealthCheck } from './commands/health-check.mjs'
|
|
|
64
66
|
import { cmdHostDetect, cmdReconcile } from './commands/host-detect.mjs'
|
|
65
67
|
import { cmdInit } from './commands/init.mjs'
|
|
66
68
|
import { cmdInitState } from './commands/init-state.mjs'
|
|
69
|
+
import { cmdInstallFromSource } from './commands/install-from-source.mjs'
|
|
67
70
|
import { cmdLock } from './commands/lock.mjs'
|
|
68
71
|
import { cmdMigratePrReviews } from './commands/migrate-pr-reviews.mjs'
|
|
69
72
|
import { cmdPersonas } from './commands/personas.mjs'
|
|
@@ -213,6 +216,18 @@ export const DISPATCH = {
|
|
|
213
216
|
'state-update': (args) => wrap(cmdStateUpdate)(args[1], args.slice(2)),
|
|
214
217
|
'state-artifacts': (args) => wrap(cmdStateArtifacts)(args[1], args.slice(2)),
|
|
215
218
|
'detect-project': (args) => wrap(cmdDetectProject)(args[1]),
|
|
219
|
+
'detect-runtime': (args) => wrap(cmdDetectRuntime)(args[1], args.slice(2)),
|
|
220
|
+
driver: (args) => {
|
|
221
|
+
// `apt-tools driver doctor <project-dir>` — only subcommand for now.
|
|
222
|
+
if (args[1] === 'doctor') return wrap(cmdDriverDoctor)(args[2], args.slice(3))
|
|
223
|
+
return wrap(() => ({
|
|
224
|
+
envelope: {
|
|
225
|
+
status: 'error',
|
|
226
|
+
error: `Usage: apt-tools driver doctor <project-dir>. Unknown subcommand: ${args[1] ?? '(none)'}`,
|
|
227
|
+
},
|
|
228
|
+
exitCode: 1,
|
|
229
|
+
}))()
|
|
230
|
+
},
|
|
216
231
|
'features-audit': (args) => wrap(cmdFeaturesAudit)(args[1], args.slice(2)),
|
|
217
232
|
'init-state': (args) => wrap(cmdInitState)(args[1], args.slice(2)),
|
|
218
233
|
'risk-check': (args) => wrap(cmdRiskCheck)(args[1], args.slice(2)),
|
|
@@ -232,6 +247,7 @@ export const DISPATCH = {
|
|
|
232
247
|
'detect-worktree': (args) => wrap(cmdDetectWorktree)(args[1]),
|
|
233
248
|
template: (args) => wrap(cmdTemplate)(args[1]),
|
|
234
249
|
'check-version': (args) => wrap(cmdCheckVersion)(args[1]),
|
|
250
|
+
'install-from-source': (args) => wrap(cmdInstallFromSource)(args[1]),
|
|
235
251
|
'health-check': (args) => wrap(cmdHealthCheck)(args[1]),
|
|
236
252
|
route: (args) => wrap(cmdRoute)(args[1], args.slice(2)),
|
|
237
253
|
statusline: (args) => wrap(cmdStatusline)(args[1], args.slice(2)),
|
|
@@ -287,7 +303,7 @@ export const DISPATCH = {
|
|
|
287
303
|
},
|
|
288
304
|
'plan-approval': (args) => wrap(cmdPlanApproval)(args[1], args[2], args.slice(3)),
|
|
289
305
|
|
|
290
|
-
// ----
|
|
306
|
+
// ---- Aperant adoption (ST-5, ST-6, ST-10, ST-13) artifact writers + discoverability ----
|
|
291
307
|
context: (args) => wrap(cmdContext)(args[1], args[2], args.slice(3)),
|
|
292
308
|
adr: (args) => wrap(cmdAdr)(args[1], args[2], args.slice(3)),
|
|
293
309
|
triage: (args) => wrap(cmdTriage)(args[1], args[2], args.slice(3)),
|
package/src/cli/gate/context.mjs
CHANGED
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
* taskId: string,
|
|
12
12
|
* taskDir: string, // <sharedRoot>/.aperant/tasks/<taskId>/
|
|
13
13
|
* taskRecord: object | null, // state.active_tasks[taskId] (or null)
|
|
14
|
-
* config: object
|
|
14
|
+
* config: object, // .aperant/config.json (empty {} if none)
|
|
15
|
+
* profile: 'lite' | 'full-pipeline'
|
|
16
|
+
* // derived from taskRecord.track:
|
|
17
|
+
* // QUICK | DEBUG → 'lite'
|
|
18
|
+
* // STANDARD | DEEP → 'full-pipeline'
|
|
19
|
+
* // null taskRecord → 'full-pipeline'
|
|
20
|
+
* // (fail-closed)
|
|
15
21
|
* }
|
|
16
22
|
*
|
|
17
23
|
* The builder is side-effect-free — it never writes state, never mutates the
|
|
@@ -24,6 +30,8 @@ import { createCoordinationStore } from '../coordination/store.mjs'
|
|
|
24
30
|
|
|
25
31
|
const SAFE_TASK_ID_PATTERN = /^[a-zA-Z0-9_-]+$/
|
|
26
32
|
|
|
33
|
+
const KNOWN_TRACKS = new Set(['QUICK', 'STANDARD', 'DEEP', 'DEBUG'])
|
|
34
|
+
|
|
27
35
|
function readJsonIfExists(path) {
|
|
28
36
|
if (!existsSync(path)) return null
|
|
29
37
|
try {
|
|
@@ -81,6 +89,16 @@ export function buildGateContext(projectRoot, taskId) {
|
|
|
81
89
|
config = readJsonIfExists(join(absRoot, '.aperant', 'config.json')) ?? {}
|
|
82
90
|
}
|
|
83
91
|
|
|
92
|
+
if (taskRecord?.track && !KNOWN_TRACKS.has(taskRecord.track)) {
|
|
93
|
+
process.stderr.write(
|
|
94
|
+
`[apt:gate] WARNING: unrecognized track "${taskRecord.track}" — defaulting to full-pipeline. ` +
|
|
95
|
+
`Expected one of: ${[...KNOWN_TRACKS].join(', ')}.\n`,
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const profile =
|
|
100
|
+
taskRecord?.track === 'QUICK' || taskRecord?.track === 'DEBUG' ? 'lite' : 'full-pipeline'
|
|
101
|
+
|
|
84
102
|
return {
|
|
85
103
|
projectRoot: absRoot,
|
|
86
104
|
sharedRoot,
|
|
@@ -88,5 +106,6 @@ export function buildGateContext(projectRoot, taskId) {
|
|
|
88
106
|
taskDir,
|
|
89
107
|
taskRecord,
|
|
90
108
|
config,
|
|
109
|
+
profile,
|
|
91
110
|
}
|
|
92
111
|
}
|
|
@@ -23,6 +23,7 @@ const gate = {
|
|
|
23
23
|
description: 'Cross-artifact consistency (spec/plan/review/qa/progress) has no error drift.',
|
|
24
24
|
severity: 'blocking',
|
|
25
25
|
skills: ['ship'],
|
|
26
|
+
applies_to_profile: (ctx) => ctx.profile === 'full-pipeline',
|
|
26
27
|
async check(ctx) {
|
|
27
28
|
const inputs = [
|
|
28
29
|
join(ctx.taskDir, 'spec.md'),
|
|
@@ -26,6 +26,7 @@ const gate = {
|
|
|
26
26
|
description: 'review.json has no unresolved critical/major findings (skipped if absent)',
|
|
27
27
|
severity: 'blocking',
|
|
28
28
|
skills: ['ship'],
|
|
29
|
+
applies_to_profile: (ctx) => ctx.profile === 'full-pipeline',
|
|
29
30
|
async check(ctx) {
|
|
30
31
|
const path = join(ctx.taskDir, 'review.json')
|
|
31
32
|
const inputs = [path]
|
|
@@ -53,6 +53,7 @@ const gate = {
|
|
|
53
53
|
'qa_signoff.json exists with verdict === "approved" (or "approved-with-notes" when configured)',
|
|
54
54
|
severity: 'blocking',
|
|
55
55
|
skills: ['ship'],
|
|
56
|
+
applies_to_profile: (ctx) => ctx.profile === 'full-pipeline',
|
|
56
57
|
async check(ctx) {
|
|
57
58
|
const path = join(ctx.taskDir, 'qa_signoff.json')
|
|
58
59
|
const inputs = [path]
|
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
* description: string, // human-readable one-liner
|
|
11
11
|
* severity: 'blocking' | 'warn', // blocking gates halt the skill
|
|
12
12
|
* skills: string[], // skills this gate fires for
|
|
13
|
-
* check: async (ctx) => GateVerdict
|
|
13
|
+
* check: async (ctx) => GateVerdict,
|
|
14
|
+
* applies_to_profile?: (ctx) => boolean // optional; when present and
|
|
15
|
+
* // returns false, the gate is
|
|
16
|
+
* // skipped before cache lookup or
|
|
17
|
+
* // check() invocation. Omit to
|
|
18
|
+
* // apply universally (default).
|
|
14
19
|
* }
|
|
15
20
|
*
|
|
16
21
|
* A GateVerdict is:
|
|
@@ -57,6 +62,11 @@ export function registerGate(gate) {
|
|
|
57
62
|
if (typeof gate.check !== 'function') {
|
|
58
63
|
throw new Error(`registerGate(${gate.id}): check must be a function`)
|
|
59
64
|
}
|
|
65
|
+
if (gate.applies_to_profile !== undefined && typeof gate.applies_to_profile !== 'function') {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`registerGate(${gate.id}): applies_to_profile, when present, must be a function`,
|
|
68
|
+
)
|
|
69
|
+
}
|
|
60
70
|
registry.set(gate.id, gate)
|
|
61
71
|
return gate
|
|
62
72
|
}
|
package/src/cli/host/detect.mjs
CHANGED
|
@@ -345,6 +345,6 @@ function detectRawCli(partner, pathEntries, exists) {
|
|
|
345
345
|
function rawCliInvocation(partner) {
|
|
346
346
|
if (partner === 'codex') return 'codex exec'
|
|
347
347
|
if (partner === 'claude') return 'claude -p'
|
|
348
|
-
if (partner === 'gemini') return 'gemini
|
|
348
|
+
if (partner === 'gemini') return 'gemini'
|
|
349
349
|
return partner
|
|
350
350
|
}
|