@aperant/framework 0.8.0 → 0.8.1
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 +24 -0
- package/README.md +12 -0
- package/dist/cli/commands/init.d.mts.map +1 -1
- package/dist/cli/commands/init.mjs +67 -27
- package/dist/cli/commands/init.mjs.map +1 -1
- package/dist/cli/design/frontmatter-schema.d.mts +4 -4
- package/dist/cli/install/cli-to-install-root.d.mts.map +1 -1
- package/dist/cli/install/cli-to-install-root.mjs +0 -2
- package/dist/cli/install/cli-to-install-root.mjs.map +1 -1
- package/dist/cli/install/runtime-detect.d.mts +2 -6
- package/dist/cli/install/runtime-detect.d.mts.map +1 -1
- package/dist/cli/install/runtime-detect.mjs +8 -3
- package/dist/cli/install/runtime-detect.mjs.map +1 -1
- package/dist/cli/install/transforms/pi.d.mts +6 -0
- package/dist/cli/install/transforms/pi.d.mts.map +1 -0
- package/dist/cli/install/transforms/pi.mjs +37 -0
- package/dist/cli/install/transforms/pi.mjs.map +1 -0
- package/dist/cli/personas/sidecar.d.mts +1 -1
- package/dist/cli/roadmap/lifecycle.d.mts +1 -1
- package/dist/cli/roadmap/rollup.d.mts +2 -2
- package/dist/plugin/.claude-plugin/plugin.json +2 -1
- package/dist/plugin/agents/apt-executor.md +12 -0
- package/dist/plugin/agents/apt-team-docs-narrator.md +11 -0
- package/dist/plugin/skills/apt/SKILL.md +3 -2
- package/dist/plugin/skills/apt-execute/SKILL.md +13 -0
- package/dist/plugin/skills/apt-fan-out/SKILL.md +124 -0
- package/dist/plugin/skills/apt-pr-review/SKILL.md +8 -1
- package/dist/plugin/skills/apt-review/SKILL.md +4 -0
- package/dist/plugin/skills/apt-run/SKILL.md +20 -5
- package/dist/plugin/skills/apt-spar/SKILL.md +12 -9
- package/dist/plugin/skills/apt-verify/SKILL.md +11 -0
- package/dist/plugin/skills/apt-watch-ci/SKILL.md +2 -2
- package/dist/schemas/quick-task.d.ts +17 -17
- package/package.json +1 -1
- package/skills/apt/SKILL.md +3 -2
- package/src/cli/commands/init.mjs +66 -27
- package/src/cli/install/cli-to-install-root.mjs +0 -2
- package/src/cli/install/runtime-detect.mjs +8 -3
- package/src/cli/install/transforms/pi.mjs +37 -0
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* rewrite, 2026-04).
|
|
4
4
|
*
|
|
5
5
|
* Replaces the previous Claude-only `cp -r` installer with the per-runtime
|
|
6
|
-
* transform pipeline (src/cli/install/pipeline.mjs).
|
|
7
|
-
* supported via `--<runtime>` flags or `--all`. Default with no
|
|
6
|
+
* transform pipeline (src/cli/install/pipeline.mjs). Registered runtime
|
|
7
|
+
* targets are supported via `--<runtime>` flags or `--all`. Default with no
|
|
8
|
+
* flags in
|
|
8
9
|
* non-TTY mode → `--claude` (backward compat — existing skills invoke
|
|
9
10
|
* `apt-tools init <dir>` with no flags). Default in TTY → interactive prompt
|
|
10
11
|
* with auto-detection markers.
|
|
@@ -40,12 +41,13 @@ import { buildAperantGitignoreTemplate } from '../config/upgrade-gitignore.mjs'
|
|
|
40
41
|
import { detectPublicRemote } from '../git/remote.mjs'
|
|
41
42
|
import { detectArchetype } from '../install/archetype.mjs'
|
|
42
43
|
import { installKernel } from '../install/install-kernel.mjs'
|
|
44
|
+
import { MANIFEST_FILENAME } from '../install/manifest.mjs'
|
|
43
45
|
import {
|
|
44
46
|
discoverCanonicalFiles,
|
|
45
47
|
runInstall,
|
|
46
48
|
writeLastInstallSummary,
|
|
47
49
|
} from '../install/pipeline.mjs'
|
|
48
|
-
import { RUNTIMES, suggestRuntimes } from '../install/runtime-detect.mjs'
|
|
50
|
+
import { getRuntime, RUNTIMES, suggestRuntimes } from '../install/runtime-detect.mjs'
|
|
49
51
|
import { runLegacyCleanup } from '../install/runtime-migrate.mjs'
|
|
50
52
|
import {
|
|
51
53
|
CLASS_A_SETTINGS,
|
|
@@ -392,6 +394,42 @@ function selectRuntimes(targetDir, parsedFlags) {
|
|
|
392
394
|
return [...suggested]
|
|
393
395
|
}
|
|
394
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Return the runtime ids that should be installed during a no-schema-drift
|
|
399
|
+
* init. A current config is not enough to prove runtime files exist on this
|
|
400
|
+
* machine: team repos often carry `.aperant/config.json` but not local
|
|
401
|
+
* runtime roots/manifests.
|
|
402
|
+
*
|
|
403
|
+
* @param {string} targetDir
|
|
404
|
+
* @param {string[]} runtimeIds
|
|
405
|
+
* @returns {string[]}
|
|
406
|
+
*/
|
|
407
|
+
function runtimesMissingManifest(targetDir, runtimeIds) {
|
|
408
|
+
const missing = []
|
|
409
|
+
for (const id of runtimeIds) {
|
|
410
|
+
const runtime = getRuntime(id)
|
|
411
|
+
if (!runtime) continue
|
|
412
|
+
const manifestPath = join(targetDir, runtime.installRoot, MANIFEST_FILENAME)
|
|
413
|
+
if (!existsSync(manifestPath)) missing.push(id)
|
|
414
|
+
}
|
|
415
|
+
return missing
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Current-schema init should still run the runtime installer when the user
|
|
420
|
+
* explicitly requested runtimes, when --force-runtimes is present, or when
|
|
421
|
+
* local runtime roots are suggested but have no apt manifest.
|
|
422
|
+
*
|
|
423
|
+
* @param {string} targetDir
|
|
424
|
+
* @param {ReturnType<typeof parseInstallFlags>} parsedFlags
|
|
425
|
+
* @returns {string[]}
|
|
426
|
+
*/
|
|
427
|
+
function selectNoDriftRuntimeInstallIds(targetDir, parsedFlags) {
|
|
428
|
+
if (parsedFlags.forceRuntimes) return selectRuntimes(targetDir, parsedFlags)
|
|
429
|
+
if (parsedFlags.runtimes_explicit) return parsedFlags.runtimes
|
|
430
|
+
return runtimesMissingManifest(targetDir, suggestRuntimes(targetDir))
|
|
431
|
+
}
|
|
432
|
+
|
|
395
433
|
/**
|
|
396
434
|
* Non-TTY / scripted path — derive answers from flags + sensible defaults,
|
|
397
435
|
* gated by the Class A/B classifier (spec R2 + R3).
|
|
@@ -663,11 +701,16 @@ async function interactiveInterview(targetDir, version, canonicalRoot, archetype
|
|
|
663
701
|
migration.applied_defaults.length === 0 &&
|
|
664
702
|
migration.auto_added.length === 0
|
|
665
703
|
if (noDrift) {
|
|
666
|
-
|
|
667
|
-
// "Nothing to do." outro so the
|
|
668
|
-
//
|
|
669
|
-
if (
|
|
670
|
-
return {
|
|
704
|
+
const runtimeInstallIds = selectNoDriftRuntimeInstallIds(targetDir, parsedFlags)
|
|
705
|
+
// Runtime repair bypasses the TTY "Nothing to do." outro so the
|
|
706
|
+
// runtime walker can re-stamp missing manifests first.
|
|
707
|
+
if (runtimeInstallIds.length > 0) {
|
|
708
|
+
return {
|
|
709
|
+
upgrade_noop: true,
|
|
710
|
+
skipBootstrap,
|
|
711
|
+
migration,
|
|
712
|
+
runtime_reinstall_runtimes: runtimeInstallIds,
|
|
713
|
+
}
|
|
671
714
|
}
|
|
672
715
|
process.stdout.write(`\n${aperantBanner(version)}\n\n`)
|
|
673
716
|
clack.intro('install')
|
|
@@ -1158,20 +1201,20 @@ export async function cmdInit(projectDir, extraArgs = []) {
|
|
|
1158
1201
|
)
|
|
1159
1202
|
}
|
|
1160
1203
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1204
|
+
const noDriftRuntimeInstallIds = answers?.upgrade_noop
|
|
1205
|
+
? (answers.runtime_reinstall_runtimes ??
|
|
1206
|
+
selectNoDriftRuntimeInstallIds(installTarget, parsedFlags))
|
|
1207
|
+
: []
|
|
1208
|
+
|
|
1209
|
+
// Upgrade no-drift with no runtime work — short-circuit before
|
|
1210
|
+
// installRuntimes. /apt:update remains the right surface for refreshing
|
|
1211
|
+
// already-manifested skills; init only repairs selected or suggested
|
|
1212
|
+
// runtime roots that lack manifests.
|
|
1164
1213
|
// The bootstrap auto-chain still runs (it's deterministic and
|
|
1165
1214
|
// idempotent — preserves PROJECT.md, refreshes sidecar from it).
|
|
1166
1215
|
// In TTY mode clack already rendered "Nothing to do." to stdout,
|
|
1167
1216
|
// so suppress the JSON envelope (matches the cancelled path).
|
|
1168
|
-
|
|
1169
|
-
// 0.6.7 (Bug 2 fix) — `--force-runtimes` bypasses this short-circuit
|
|
1170
|
-
// so `/apt:update` can re-stamp every runtime manifest even when
|
|
1171
|
-
// `.aperant/config.json` is at the current schema. We synthesize a
|
|
1172
|
-
// real InstallAnswers shape from the existing config so
|
|
1173
|
-
// installRuntimes + finishInit run their normal upgrade path.
|
|
1174
|
-
if (answers?.upgrade_noop && !parsedFlags.forceRuntimes) {
|
|
1217
|
+
if (answers?.upgrade_noop && noDriftRuntimeInstallIds.length === 0) {
|
|
1175
1218
|
if (interactiveTty) return rawText('', 0)
|
|
1176
1219
|
const bootstrap = answers.skipBootstrap
|
|
1177
1220
|
? { status: 'skipped', reason: '--skip-bootstrap flag' }
|
|
@@ -1187,18 +1230,14 @@ export async function cmdInit(projectDir, extraArgs = []) {
|
|
|
1187
1230
|
})
|
|
1188
1231
|
}
|
|
1189
1232
|
|
|
1190
|
-
//
|
|
1191
|
-
//
|
|
1192
|
-
//
|
|
1193
|
-
// cross-verification are read from answers.migration.migrated (already
|
|
1194
|
-
// in memory from nonInteractiveAnswers — avoids a second config read);
|
|
1195
|
-
// runtimes resolved via selectRuntimes helper shared with the upgrade path.
|
|
1233
|
+
// Current-schema runtime install — synthesize a real InstallAnswers
|
|
1234
|
+
// shape from the existing config so installRuntimes + finishInit run
|
|
1235
|
+
// their normal upgrade path.
|
|
1196
1236
|
let effectiveAnswers = answers
|
|
1197
|
-
if (answers?.upgrade_noop &&
|
|
1237
|
+
if (answers?.upgrade_noop && noDriftRuntimeInstallIds.length > 0) {
|
|
1198
1238
|
const existing = answers.migration?.migrated || {}
|
|
1199
|
-
const runtimes = selectRuntimes(installTarget, parsedFlags)
|
|
1200
1239
|
effectiveAnswers = {
|
|
1201
|
-
runtimes,
|
|
1240
|
+
runtimes: noDriftRuntimeInstallIds,
|
|
1202
1241
|
autonomyDefault: existing?.autonomy?.default ?? 1,
|
|
1203
1242
|
visibility: existing?.share?.visibility === 'team' ? 'team' : 'solo',
|
|
1204
1243
|
crossVerificationTools: existing?.multi_model?.cross_verification?.tools ?? [],
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
* the lookups below work automatically.
|
|
12
12
|
*
|
|
13
13
|
* `null` is returned for:
|
|
14
|
-
* - CLIs with no install target (e.g. `pi` — host detected, but no apt
|
|
15
|
-
* install root exists for the Pi runtime).
|
|
16
14
|
* - `'unknown'` — deterministic branches (init/status/help) don't need a
|
|
17
15
|
* mapping; the router falls back to the framework-scoped dev default
|
|
18
16
|
* defined in getTrustedSkillSources().
|
|
@@ -30,9 +30,7 @@ import { MANIFEST_FILENAME } from './manifest.mjs'
|
|
|
30
30
|
* @property {string|null} cliId The `cli` string `detectHost()` returns
|
|
31
31
|
* when this runtime's CLI is at the prompt.
|
|
32
32
|
* `null` when the runtime has no install
|
|
33
|
-
* target mapped to a host CLI
|
|
34
|
-
* which has a detectable host but no apt
|
|
35
|
-
* install root of its own).
|
|
33
|
+
* target mapped to a host CLI.
|
|
36
34
|
* @property {string} [displayLabel] Optional user-facing label (e.g. "Kilo Code").
|
|
37
35
|
* When unset, callers fall back to the
|
|
38
36
|
* capitalized form of `id`. Use for runtimes
|
|
@@ -77,6 +75,13 @@ export const RUNTIMES = Object.freeze([
|
|
|
77
75
|
markers: ['.opencode/config.json'],
|
|
78
76
|
cliId: 'opencode',
|
|
79
77
|
},
|
|
78
|
+
{
|
|
79
|
+
id: 'pi',
|
|
80
|
+
flag: '--pi',
|
|
81
|
+
installRoot: '.pi',
|
|
82
|
+
markers: ['.pi/settings.json', '.pi/skills', '.pi/prompts'],
|
|
83
|
+
cliId: 'pi',
|
|
84
|
+
},
|
|
80
85
|
{
|
|
81
86
|
id: 'kilo',
|
|
82
87
|
flag: '--kilo',
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* install/transforms/pi.mjs — Pi coding agent transform.
|
|
3
|
+
*
|
|
4
|
+
* Pi uses prompt templates under `.pi/prompts/` for slash commands and
|
|
5
|
+
* root-level skills under `.pi/skills/`. Unlike Codex, Pi supports
|
|
6
|
+
* colon-form prompt names, so `/apt:foo` references are preserved.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { basename, dirname } from 'node:path'
|
|
10
|
+
import { substituteVersion } from './_shared.mjs'
|
|
11
|
+
|
|
12
|
+
function promptNameForSkill(folder) {
|
|
13
|
+
if (folder === 'apt') return 'apt'
|
|
14
|
+
if (folder.startsWith('apt-')) return `apt:${folder.slice('apt-'.length)}`
|
|
15
|
+
return folder
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {import('../pipeline.mjs').TransformInput} input
|
|
20
|
+
* @returns {import('../pipeline.mjs').TransformOutput[]}
|
|
21
|
+
*/
|
|
22
|
+
export function transform({ canonicalContent, canonicalRelPath, version, fileType }) {
|
|
23
|
+
const content = substituteVersion(canonicalContent, version)
|
|
24
|
+
if (fileType === 'skill') {
|
|
25
|
+
const folder = basename(dirname(canonicalRelPath))
|
|
26
|
+
const promptName = promptNameForSkill(folder)
|
|
27
|
+
return [
|
|
28
|
+
{ outputPath: `skills/${folder}/SKILL.md`, content },
|
|
29
|
+
{ outputPath: `prompts/${promptName}.md`, content },
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
if (fileType === 'agent') {
|
|
33
|
+
const file = basename(canonicalRelPath)
|
|
34
|
+
return [{ outputPath: `skills/${file}`, content }]
|
|
35
|
+
}
|
|
36
|
+
return []
|
|
37
|
+
}
|