agentful 0.3.8 → 0.4.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/dist/index.cjs +297 -21
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -3040,11 +3040,11 @@ function banner() {
|
|
|
3040
3040
|
paint.dim(brand.tagline)
|
|
3041
3041
|
].join("\n");
|
|
3042
3042
|
}
|
|
3043
|
-
var VERSION, brand, useColor, wrap, paint, sym, ui;
|
|
3043
|
+
var VERSION, brand, useColor, wrap, paint, tuiDesign, sym, ui;
|
|
3044
3044
|
var init_branding = __esm({
|
|
3045
3045
|
"src/branding.ts"() {
|
|
3046
3046
|
"use strict";
|
|
3047
|
-
VERSION = "0.
|
|
3047
|
+
VERSION = "0.4.0" ? "0.4.0" : null.version;
|
|
3048
3048
|
brand = {
|
|
3049
3049
|
name: "agentful",
|
|
3050
3050
|
// the command users type
|
|
@@ -3067,6 +3067,12 @@ var init_branding = __esm({
|
|
|
3067
3067
|
dim: wrap("2"),
|
|
3068
3068
|
bold: wrap("1")
|
|
3069
3069
|
};
|
|
3070
|
+
tuiDesign = {
|
|
3071
|
+
/** Sits in front of the input line. U+276F, the shell prompt sigil. */
|
|
3072
|
+
promptMark: "\u276F",
|
|
3073
|
+
/** Marks an item in progress in the sidebar's task list. U+203A. */
|
|
3074
|
+
activeMark: "\u203A"
|
|
3075
|
+
};
|
|
3070
3076
|
sym = {
|
|
3071
3077
|
ok: paint.success("\u2714"),
|
|
3072
3078
|
fail: paint.error("\u2716"),
|
|
@@ -3504,40 +3510,289 @@ function brandingPluginSource() {
|
|
|
3504
3510
|
const logoLiteral = JSON.stringify(logo_default.replace(/\n+$/, "").split("\n"));
|
|
3505
3511
|
return `// Generated by ${brand.name} v${VERSION} \u2014 do not edit; rewritten on every start.
|
|
3506
3512
|
const LOGO = ${logoLiteral}
|
|
3513
|
+
const MARK = ${JSON.stringify(tuiDesign.promptMark)}
|
|
3514
|
+
const ACTIVE = ${JSON.stringify(tuiDesign.activeMark)}
|
|
3515
|
+
const USD = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' })
|
|
3516
|
+
|
|
3517
|
+
// Ours, not the engine's generic examples ("Fix a TODO in the codebase").
|
|
3518
|
+
const PLACEHOLDERS = {
|
|
3519
|
+
normal: [
|
|
3520
|
+
'Build a landing page for my newsletter',
|
|
3521
|
+
'Add a contact form and push it live',
|
|
3522
|
+
'What changed since my last push?',
|
|
3523
|
+
],
|
|
3524
|
+
shell: ['agentful push', 'agentful open', 'git status'],
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
/** Engine state reads are best-effort: a renamed field must not blank the UI. */
|
|
3528
|
+
function safe(fn, fallback) {
|
|
3529
|
+
try {
|
|
3530
|
+
const value = fn()
|
|
3531
|
+
return value === undefined || value === null ? fallback : value
|
|
3532
|
+
} catch (err) {
|
|
3533
|
+
return fallback
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
/** Keep the tail of a path \u2014 the file name matters more than its parent. */
|
|
3538
|
+
function tail(text, max) {
|
|
3539
|
+
const s = String(text)
|
|
3540
|
+
return s.length <= max ? s : '\u2026' + s.slice(s.length - (max - 1))
|
|
3541
|
+
}
|
|
3507
3542
|
|
|
3508
3543
|
const plugin = {
|
|
3509
3544
|
id: 'agentful-branding',
|
|
3510
3545
|
tui(api) {
|
|
3546
|
+
const Prompt = api.ui.Prompt
|
|
3547
|
+
const HostSlot = api.ui.Slot
|
|
3548
|
+
// The slot context carries no colors; the theme lives on the plugin api.
|
|
3549
|
+
const t = () => api.theme.current
|
|
3550
|
+
|
|
3551
|
+
// \u2500\u2500 band pieces \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3552
|
+
// The engine closes its prompt with a heavy line (an upper-half block, not
|
|
3553
|
+
// a box-drawing rune), so the band's own top line has to match it or the
|
|
3554
|
+
// two read as unrelated. Sidebar dividers stay thin: they repeat four
|
|
3555
|
+
// times and must not compete with the input.
|
|
3556
|
+
const HEAVY = {
|
|
3557
|
+
topLeft: '\u2580', topRight: '\u2580', bottomLeft: '\u2580', bottomRight: '\u2580',
|
|
3558
|
+
horizontal: '\u2580', vertical: '\u2580',
|
|
3559
|
+
topT: '\u2580', bottomT: '\u2580', leftT: '\u2580', rightT: '\u2580', cross: '\u2580',
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3562
|
+
function Rule(props) {
|
|
3563
|
+
return (
|
|
3564
|
+
<box
|
|
3565
|
+
height={1}
|
|
3566
|
+
flexShrink={0}
|
|
3567
|
+
border={['top']}
|
|
3568
|
+
borderColor={props.heavy ? t().border : t().borderSubtle}
|
|
3569
|
+
customBorderChars={props.heavy ? HEAVY : undefined}
|
|
3570
|
+
/>
|
|
3571
|
+
)
|
|
3572
|
+
}
|
|
3573
|
+
|
|
3574
|
+
function Row(props) {
|
|
3575
|
+
return (
|
|
3576
|
+
<box flexDirection="row" justifyContent="space-between" gap={1}>
|
|
3577
|
+
<text fg={t().textMuted} wrapMode="none">{props.label}</text>
|
|
3578
|
+
<text fg={props.fg || t().text} flexShrink={0} wrapMode="none">{props.value}</text>
|
|
3579
|
+
</box>
|
|
3580
|
+
)
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3583
|
+
function Section(props) {
|
|
3584
|
+
return (
|
|
3585
|
+
<box flexDirection="column" flexShrink={0}>
|
|
3586
|
+
<Rule />
|
|
3587
|
+
<text fg={t().textMuted}>{props.label}</text>
|
|
3588
|
+
{props.children}
|
|
3589
|
+
</box>
|
|
3590
|
+
)
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
// \u2500\u2500 sidebar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3594
|
+
function Sidebar(props) {
|
|
3595
|
+
const messages = () => safe(() => api.state.session.messages(props.sessionID), [])
|
|
3596
|
+
const usage = () => safe(() => {
|
|
3597
|
+
const last = messages().findLast((m) => m.role === 'assistant' && m.tokens && m.tokens.output > 0)
|
|
3598
|
+
if (!last) return { tokens: 0, percent: null }
|
|
3599
|
+
const tk = last.tokens
|
|
3600
|
+
const total = tk.input + tk.output + tk.reasoning + tk.cache.read + tk.cache.write
|
|
3601
|
+
const provider = api.state.provider.find((p) => p.id === last.providerID)
|
|
3602
|
+
const model = provider && provider.models ? provider.models[last.modelID] : undefined
|
|
3603
|
+
const limit = model && model.limit ? model.limit.context : 0
|
|
3604
|
+
return { tokens: total, percent: limit ? Math.round((total / limit) * 100) : null }
|
|
3605
|
+
}, { tokens: 0, percent: null })
|
|
3606
|
+
// Only shown once it is real: platform models bill credits server-side
|
|
3607
|
+
// and report 0 here, and a permanent "$0.00" reads like a promise.
|
|
3608
|
+
const cost = () => safe(() => api.state.session.get(props.sessionID).cost, 0)
|
|
3609
|
+
const lsp = () => safe(() => api.state.lsp(), [])
|
|
3610
|
+
const lspDisabled = () => safe(() => !api.state.config.lsp, true)
|
|
3611
|
+
const mcp = () => safe(() => api.state.mcp(), [])
|
|
3612
|
+
const todos = () => safe(() => api.state.session.todo(props.sessionID), [])
|
|
3613
|
+
const openTodos = () => todos().filter((item) => item.status !== 'completed')
|
|
3614
|
+
const files = () => safe(() => api.state.session.diff(props.sessionID), [])
|
|
3615
|
+
|
|
3616
|
+
return (
|
|
3617
|
+
<box flexDirection="column" gap={1} flexShrink={0}>
|
|
3618
|
+
<Section label="CONTEXT">
|
|
3619
|
+
<Row label="tokens" value={() => usage().tokens.toLocaleString('en-US')} />
|
|
3620
|
+
{() => usage().percent === null
|
|
3621
|
+
? null
|
|
3622
|
+
: <Row label="window" value={() => usage().percent + '%'} />}
|
|
3623
|
+
{() => cost() > 0
|
|
3624
|
+
? <Row label="cost" value={() => USD.format(cost())} />
|
|
3625
|
+
: null}
|
|
3626
|
+
</Section>
|
|
3627
|
+
|
|
3628
|
+
<Section label="TOOLS">
|
|
3629
|
+
<Row
|
|
3630
|
+
label="language server"
|
|
3631
|
+
value={() => lsp().length
|
|
3632
|
+
? lsp().filter((s) => s.status === 'connected').length + '/' + lsp().length
|
|
3633
|
+
: (lspDisabled() ? 'off' : 'idle')}
|
|
3634
|
+
/>
|
|
3635
|
+
{() => mcp().length
|
|
3636
|
+
? <Row
|
|
3637
|
+
label="mcp"
|
|
3638
|
+
value={() => mcp().filter((s) => s.status === 'connected').length + '/' + mcp().length}
|
|
3639
|
+
/>
|
|
3640
|
+
: null}
|
|
3641
|
+
</Section>
|
|
3642
|
+
|
|
3643
|
+
{() => openTodos().length === 0 ? null : (
|
|
3644
|
+
<Section label="TASKS">
|
|
3645
|
+
{() => todos().slice(0, 8).map((item) => (
|
|
3646
|
+
<box flexDirection="row" gap={1}>
|
|
3647
|
+
<text
|
|
3648
|
+
flexShrink={0}
|
|
3649
|
+
fg={item.status === 'completed'
|
|
3650
|
+
? t().success
|
|
3651
|
+
: item.status === 'in_progress' ? t().primary : t().textMuted}
|
|
3652
|
+
>
|
|
3653
|
+
{item.status === 'completed' ? '\u2713' : item.status === 'in_progress' ? ACTIVE : '\xB7'}
|
|
3654
|
+
</text>
|
|
3655
|
+
<text fg={item.status === 'completed' ? t().textMuted : t().text}>{item.content}</text>
|
|
3656
|
+
</box>
|
|
3657
|
+
))}
|
|
3658
|
+
{() => todos().length > 8
|
|
3659
|
+
? <text fg={t().textMuted}>{'+' + (todos().length - 8) + ' more'}</text>
|
|
3660
|
+
: null}
|
|
3661
|
+
</Section>
|
|
3662
|
+
)}
|
|
3663
|
+
|
|
3664
|
+
{() => files().length === 0 ? null : (
|
|
3665
|
+
<Section label="CHANGES">
|
|
3666
|
+
{() => files().slice(0, 8).map((file) => (
|
|
3667
|
+
<box flexDirection="row" gap={1} justifyContent="space-between">
|
|
3668
|
+
<text fg={t().text} wrapMode="none">{tail(file.file, 24)}</text>
|
|
3669
|
+
<box flexDirection="row" gap={1} flexShrink={0}>
|
|
3670
|
+
{file.additions ? <text fg={t().diffAdded}>{'+' + file.additions}</text> : null}
|
|
3671
|
+
{file.deletions ? <text fg={t().diffRemoved}>{'-' + file.deletions}</text> : null}
|
|
3672
|
+
</box>
|
|
3673
|
+
</box>
|
|
3674
|
+
))}
|
|
3675
|
+
{() => files().length > 8
|
|
3676
|
+
? <text fg={t().textMuted}>{'+' + (files().length - 8) + ' more'}</text>
|
|
3677
|
+
: null}
|
|
3678
|
+
</Section>
|
|
3679
|
+
)}
|
|
3680
|
+
</box>
|
|
3681
|
+
)
|
|
3682
|
+
}
|
|
3683
|
+
|
|
3684
|
+
// \u2500\u2500 prompt \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3685
|
+
/** Left half of the line under the input; replaces the engine's plain cwd. */
|
|
3686
|
+
function Hint(props) {
|
|
3687
|
+
const directory = () => safe(
|
|
3688
|
+
() => api.state.session.get(props.sessionID).directory,
|
|
3689
|
+
safe(() => api.state.path.directory, ''),
|
|
3690
|
+
)
|
|
3691
|
+
const branch = () => safe(() => api.state.vcs.branch, '')
|
|
3692
|
+
return (
|
|
3693
|
+
<box flexDirection="row" gap={1} marginLeft={1}>
|
|
3694
|
+
<text fg={t().textMuted} wrapMode="none">{() => directory()}</text>
|
|
3695
|
+
{() => branch() ? <text fg={t().textMuted} wrapMode="none">{'\xB7 ' + branch()}</text> : null}
|
|
3696
|
+
</box>
|
|
3697
|
+
)
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
/**
|
|
3701
|
+
* The band: our hairline on top, the marker in the gutter, and the
|
|
3702
|
+
* engine's own input between them. The rule sits INSIDE the right column
|
|
3703
|
+
* so it starts exactly where the engine draws its own rule under the
|
|
3704
|
+
* input \u2014 the two lines of the band must share a left edge. The marker is
|
|
3705
|
+
* pushed down by two rows for the same reason: one for the rule, one for
|
|
3706
|
+
* the padding the engine's prompt adds above its first line.
|
|
3707
|
+
*/
|
|
3708
|
+
function Band(props) {
|
|
3709
|
+
return (
|
|
3710
|
+
<box flexDirection="row" width="100%">
|
|
3711
|
+
<box flexShrink={0} paddingTop={2} paddingRight={1}>
|
|
3712
|
+
<text fg={t().primary}>{MARK}</text>
|
|
3713
|
+
</box>
|
|
3714
|
+
<box flexGrow={1} flexDirection="column">
|
|
3715
|
+
<Rule heavy />
|
|
3716
|
+
{props.children}
|
|
3717
|
+
</box>
|
|
3718
|
+
</box>
|
|
3719
|
+
)
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3511
3722
|
api.slots.register({
|
|
3512
3723
|
order: 1,
|
|
3513
3724
|
slots: {
|
|
3514
|
-
home_logo(
|
|
3515
|
-
const accent = ctx && ctx.theme ? ctx.theme.primary : undefined
|
|
3516
|
-
const muted = ctx && ctx.theme ? ctx.theme.textMuted : undefined
|
|
3725
|
+
home_logo() {
|
|
3517
3726
|
return (
|
|
3518
3727
|
<box flexDirection="column">
|
|
3519
3728
|
{LOGO.map((line) => (
|
|
3520
|
-
<text fg={
|
|
3729
|
+
<text fg={t().primary}>{line}</text>
|
|
3521
3730
|
))}
|
|
3522
3731
|
<text> </text>
|
|
3523
|
-
<text fg={
|
|
3732
|
+
<text fg={t().textMuted}>${brand.tagline}</text>
|
|
3524
3733
|
</box>
|
|
3525
3734
|
)
|
|
3526
3735
|
},
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
return <text fg={
|
|
3736
|
+
|
|
3737
|
+
home_footer() {
|
|
3738
|
+
return <text fg={t().textMuted}>${brand.name} v${VERSION}</text>
|
|
3739
|
+
},
|
|
3740
|
+
|
|
3741
|
+
home_prompt(ctx, data) {
|
|
3742
|
+
return (
|
|
3743
|
+
<Band>
|
|
3744
|
+
<Prompt
|
|
3745
|
+
ref={data.ref}
|
|
3746
|
+
placeholders={PLACEHOLDERS}
|
|
3747
|
+
right={<HostSlot name="home_prompt_right" />}
|
|
3748
|
+
/>
|
|
3749
|
+
</Band>
|
|
3750
|
+
)
|
|
3530
3751
|
},
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
sidebar_footer(ctx) {
|
|
3534
|
-
const accent = ctx && ctx.theme ? ctx.theme.primary : undefined
|
|
3535
|
-
const muted = ctx && ctx.theme ? ctx.theme.textMuted : undefined
|
|
3752
|
+
|
|
3753
|
+
session_prompt(ctx, data) {
|
|
3536
3754
|
return (
|
|
3537
|
-
<
|
|
3538
|
-
<
|
|
3539
|
-
|
|
3540
|
-
|
|
3755
|
+
<Band>
|
|
3756
|
+
<Prompt
|
|
3757
|
+
sessionID={data.session_id}
|
|
3758
|
+
visible={data.visible}
|
|
3759
|
+
disabled={data.disabled}
|
|
3760
|
+
onSubmit={data.on_submit}
|
|
3761
|
+
ref={data.ref}
|
|
3762
|
+
placeholders={PLACEHOLDERS}
|
|
3763
|
+
hint={<Hint sessionID={data.session_id} />}
|
|
3764
|
+
right={<HostSlot name="session_prompt_right" session_id={data.session_id} />}
|
|
3765
|
+
/>
|
|
3766
|
+
</Band>
|
|
3767
|
+
)
|
|
3768
|
+
},
|
|
3769
|
+
|
|
3770
|
+
sidebar_title(ctx, data) {
|
|
3771
|
+
return (
|
|
3772
|
+
<box flexDirection="column">
|
|
3773
|
+
<text fg={t().text}>{data.title}</text>
|
|
3774
|
+
{() => data.share_url
|
|
3775
|
+
? <text fg={t().accent} wrapMode="none">{data.share_url}</text>
|
|
3776
|
+
: null}
|
|
3777
|
+
</box>
|
|
3778
|
+
)
|
|
3779
|
+
},
|
|
3780
|
+
|
|
3781
|
+
sidebar_content(ctx, data) {
|
|
3782
|
+
return <Sidebar sessionID={data.session_id} />
|
|
3783
|
+
},
|
|
3784
|
+
|
|
3785
|
+
// Bottom of the sidebar \u2014 where the engine prints its own name and
|
|
3786
|
+
// version (for example "OpenCode 1.18.18"); ours replaces it.
|
|
3787
|
+
sidebar_footer() {
|
|
3788
|
+
return (
|
|
3789
|
+
<box flexDirection="column">
|
|
3790
|
+
<Rule />
|
|
3791
|
+
<text>
|
|
3792
|
+
<span fg={t().primary}>${brand.name}</span>
|
|
3793
|
+
<span fg={t().textMuted}> v${VERSION}</span>
|
|
3794
|
+
</text>
|
|
3795
|
+
</box>
|
|
3541
3796
|
)
|
|
3542
3797
|
},
|
|
3543
3798
|
},
|
|
@@ -6753,6 +7008,10 @@ is fine \u2014 but then say clearly that \`agentful push\` cannot deploy it.
|
|
|
6753
7008
|
vars (\`database.anon_key\`/\`publishable_key\` expanded as
|
|
6754
7009
|
\`VITE_\`/\`PUBLIC_\`/\`NEXT_PUBLIC_\`). Never rely on \`.env*\` files reaching the
|
|
6755
7010
|
cloud \u2014 the CLI and server both strip them.
|
|
7011
|
+
- \`.agentful/\` is local CLI control metadata and is excluded from the cloud
|
|
7012
|
+
build source. You may read \`.agentful/project.json\` while authoring, but app
|
|
7013
|
+
code must never import or fetch it. Copy the fixed project ID into an ordinary
|
|
7014
|
+
source constant when the managed backend client needs it.
|
|
6756
7015
|
|
|
6757
7016
|
## Supported frameworks (the cloud build table)
|
|
6758
7017
|
|
|
@@ -6778,7 +7037,9 @@ process, and never a script the user must run locally to produce the result.
|
|
|
6778
7037
|
|
|
6779
7038
|
When the project uses the platform's managed backend, its ONLY API surface is:
|
|
6780
7039
|
|
|
6781
|
-
- Base: \`/api/p/{projectId}/\` \u2014 projectId
|
|
7040
|
+
- Base: \`/api/p/{projectId}/\` \u2014 while authoring, read projectId from
|
|
7041
|
+
\`.agentful/project.json\` and materialize it in a normal app source constant;
|
|
7042
|
+
never import \`.agentful/\` from app code.
|
|
6782
7043
|
- End-user auth: \`\u2026/auth/*\` (\`register\`, \`login\`, \`me\`, \`verify\`, \u2026).
|
|
6783
7044
|
- Data CRUD: \`\u2026/data/{collection}\` and \`\u2026/data/{collection}/{docId}\`.
|
|
6784
7045
|
- Managed Server Actions: \`POST \u2026/actions/{action-name}\` \u2014 and NOTHING else.
|
|
@@ -6877,6 +7138,13 @@ ${backendSessionLines(backend)}
|
|
|
6877
7138
|
|
|
6878
7139
|
// src/lib/engineConfig.ts
|
|
6879
7140
|
var PROVIDER_ID = "agentful";
|
|
7141
|
+
var DISABLED_ENGINE_PLUGINS = {
|
|
7142
|
+
"internal:sidebar-context": false,
|
|
7143
|
+
"internal:sidebar-lsp": false,
|
|
7144
|
+
"internal:sidebar-mcp": false,
|
|
7145
|
+
"internal:sidebar-todo": false,
|
|
7146
|
+
"internal:sidebar-files": false
|
|
7147
|
+
};
|
|
6880
7148
|
var CLOUD_AGENT = "agentful-build";
|
|
6881
7149
|
var LOCAL_AGENT = "local-only";
|
|
6882
7150
|
var CLOUD_AGENT_PROMPT = `You are the Agentful Cloud build agent: everything you build in this directory targets deployment on Agentful Cloud via \`agentful push\`. Follow the platform contract in AGENTFUL_CLOUD.md strictly \u2014 static files only, no server process, no build-time env vars or secrets. When a push or cloud build fails, diagnose ONLY from the \`agentful push\` output and \`agentful build-status\`; quote the record and never invent causes or settings it does not show. If the user wants development that is not meant for the platform, suggest the ${LOCAL_AGENT} agent (Tab).`;
|
|
@@ -7006,7 +7274,15 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
|
|
|
7006
7274
|
(0, import_node_fs12.writeFileSync)(pluginPath, brandingPluginSource2());
|
|
7007
7275
|
(0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(configDir2, "tui.json"), JSON.stringify({
|
|
7008
7276
|
theme: "agentful",
|
|
7009
|
-
plugin: [`file://${pluginPath}`]
|
|
7277
|
+
plugin: [`file://${pluginPath}`],
|
|
7278
|
+
// The engine's own sidebar sections are plugins with fixed ids, and
|
|
7279
|
+
// sidebar_content composes rather than replaces — without switching them
|
|
7280
|
+
// off, ours would render UNDER theirs and the column would show every
|
|
7281
|
+
// number twice. `plugin_enabled` is keyed by plugin id and applies to the
|
|
7282
|
+
// built-in ones too (verified in the v1.18.18 binary); a user's own toggle
|
|
7283
|
+
// in the plugin manager still wins, because the engine merges its kv
|
|
7284
|
+
// state over this map.
|
|
7285
|
+
plugin_enabled: DISABLED_ENGINE_PLUGINS
|
|
7010
7286
|
}, null, 2));
|
|
7011
7287
|
(0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
|
|
7012
7288
|
for (const name of Object.keys(SLASH_COMMANDS)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentful",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Agentful in your terminal — local development with push-to-cloud previews",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://agentful.dev",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"sync-starters": "node scripts/sync-starters.mjs",
|
|
24
24
|
"sync-skills": "node scripts/sync-skills.mjs",
|
|
25
25
|
"typecheck": "tsc --noEmit",
|
|
26
|
+
"check-engine": "node scripts/check-engine-contract.mjs",
|
|
26
27
|
"dev": "tsx src/index.ts",
|
|
27
28
|
"sync-theme": "cp src/branding/agentful-theme.json ../frontend/public/cli/theme.json",
|
|
28
29
|
"test": "node --import tsx --test src/lib/*.test.ts"
|