hotmilk 0.1.13 → 0.1.14
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.md +3 -2
- package/README.md +167 -22
- package/agents/README.md +3 -3
- package/agents/coach.md +2 -1
- package/agents/planner.md +2 -2
- package/hotmilk.json +6 -0
- package/package.json +25 -22
- package/prompts/prompt-eval.md +241 -0
- package/prompts/tidy.md +3 -1
- package/prompts/translate.md +3 -1
- package/skills/make-docs/SKILL.md +43 -0
- package/skills/pioneer/SKILL.md +120 -130
- package/skills/pioneer/references/autoresearch-routing.md +112 -0
- package/skills/pioneer/references/chat-plan.md +50 -0
- package/skills/pioneer/references/goal-gate.md +54 -0
- package/skills/pioneer/references/graph-recon-gate.md +50 -0
- package/skills/pioneer/references/openspec-routing.md +50 -0
- package/skills/pioneer/references/plannotator-routing.md +81 -0
- package/skills/pioneer/references/prompt-eval-gate.md +38 -0
- package/skills/recommend-research/SKILL.md +94 -6
- package/skills/recommend-research/references/example-narrow.md +53 -0
- package/skills/recommend-research/references/scope-gate.md +46 -0
- package/skills/recommend-research/references/shortlist-gate.md +69 -0
- package/skills/recommend-research/references/verify-gate.md +73 -0
- package/skills/update-docs/SKILL.md +86 -0
- package/skills/update-docs/references/doc-inventory.md +69 -0
- package/skills/update-docs/references/drift-verification.md +59 -0
- package/src/bootstrap/extensions.ts +7 -1
- package/src/bootstrap/global-extension-sources.ts +10 -1
- package/src/bootstrap/project-trust.ts +41 -0
- package/src/bootstrap/session.ts +28 -1
- package/src/config/bundled-extensions.ts +12 -0
- package/src/config/hotmilk.ts +22 -0
- package/src/config/resolve.ts +16 -0
- package/src/config/runtime.ts +4 -0
- package/src/index.ts +4 -0
- package/themes/monokai.json +1 -1
- package/skills/empirical-prompt-tuning/SKILL.md +0 -232
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Doc inventory
|
|
2
|
+
|
|
3
|
+
**Load when:** Phase 0 (full sync), or you need the truth → doc matrix for an unfamiliar drift.
|
|
4
|
+
|
|
5
|
+
**Do NOT load when:** Post-ship route already names the row (read that row here only if unsure), or the user scoped a single file you already know.
|
|
6
|
+
|
|
7
|
+
Code and config win. Docs below are reconciliation targets. **Adapt rows to the repo** — skip rows that do not exist.
|
|
8
|
+
|
|
9
|
+
## Detect project type (Phase 0)
|
|
10
|
+
|
|
11
|
+
Read one manifest first, then use the matching rows:
|
|
12
|
+
|
|
13
|
+
| Signal file | Project type |
|
|
14
|
+
| ----------------------------- | -------------- |
|
|
15
|
+
| `package.json` | Node / JS / TS |
|
|
16
|
+
| `Cargo.toml` | Rust |
|
|
17
|
+
| `pyproject.toml` / `setup.py` | Python |
|
|
18
|
+
| `go.mod` | Go |
|
|
19
|
+
| `pom.xml` / `build.gradle*` | JVM |
|
|
20
|
+
|
|
21
|
+
Also note doc roots if present: `README.md`, `CONTRIBUTING.md`, `docs/`, `CHANGELOG.md`, and any root instruction files (`AGENTS.md`, `CLAUDE.md`, …).
|
|
22
|
+
|
|
23
|
+
**Monorepo / multi-manifest:** When root and package manifests both exist, treat **root manifest scripts + root README/CONTRIBUTING** as canonical for repo-wide commands. Package READMEs get package-local scripts and paths only — do not copy root commands into every package README unless that package owns them.
|
|
24
|
+
|
|
25
|
+
## Truth → doc matrix
|
|
26
|
+
|
|
27
|
+
| Source of truth | What to extract | Doc targets |
|
|
28
|
+
| ------------------------------------------------------------------------------ | --------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
29
|
+
| Package manifest (`package.json`, `Cargo.toml`, `pyproject.toml`, `go.mod`, …) | Scripts/commands, deps, version, entry points, published files | `README.md`, `CONTRIBUTING.md` |
|
|
30
|
+
| Source layout (`src/`, `lib/`, `cmd/`, …) | Module roles, public API surface | README architecture section, `docs/` |
|
|
31
|
+
| CI config (`.github/workflows/`, `.gitlab-ci.yml`, …) | Test/lint/build commands | `README.md`, `CONTRIBUTING.md` |
|
|
32
|
+
| Config templates in repo root | Default values users copy | README config sample, setup docs |
|
|
33
|
+
| `CHANGELOG.md` / release tags | Version, breaking changes | README version notes |
|
|
34
|
+
| `docs/` (when directory exists) | Topic pages, API reference | Cross-link from README; do not create `docs/` during sync |
|
|
35
|
+
| Root instruction files (when present) | Commands, layout tables, contributor rules | That file only — do not invent paths |
|
|
36
|
+
| Workspace / monorepo (root + package manifests) | Root scripts for repo-wide ops; package scripts for package-local ops | Root README/CONTRIBUTING ↔ root manifest; package README ↔ that package manifest |
|
|
37
|
+
|
|
38
|
+
## Post-ship triggers (targeted recon)
|
|
39
|
+
|
|
40
|
+
When the user changed code recently, scan **only** rows touched by the change:
|
|
41
|
+
|
|
42
|
+
| Change area | Minimum doc pass |
|
|
43
|
+
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
|
|
44
|
+
| New dependency or entry point | Manifest ↔ README (+ CONTRIBUTING if it lists deps) |
|
|
45
|
+
| Default config flip or toggle default change | Config template ↔ README sample and feature/toggle tables |
|
|
46
|
+
| Lazy-loaded or opt-in feature (doc mentions it; default off in template) | Template defaults ↔ README — mark opt-in/experimental when default is false |
|
|
47
|
+
| New script or test command | Manifest scripts ↔ README + CONTRIBUTING |
|
|
48
|
+
| Version / peer bump | README version note, manifest version constraints |
|
|
49
|
+
| Public API or module layout change | Layout tables, `docs/` API pages |
|
|
50
|
+
| CI workflow change | CONTRIBUTING / README verify commands |
|
|
51
|
+
|
|
52
|
+
## Full-sync order
|
|
53
|
+
|
|
54
|
+
1. Detect project type; read truth files for applicable matrix rows.
|
|
55
|
+
2. Read every doc target listed for those rows.
|
|
56
|
+
3. Build a diff list — fact mismatches only.
|
|
57
|
+
4. Patch the smallest set of files that restores consistency.
|
|
58
|
+
5. Run [`drift-verification.md`](drift-verification.md).
|
|
59
|
+
|
|
60
|
+
## Common drifts (quick scan)
|
|
61
|
+
|
|
62
|
+
| Symptom | Likely fix |
|
|
63
|
+
| ---------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
64
|
+
| README command does not exist in manifest | Align with manifest scripts/targets |
|
|
65
|
+
| Doc paths point to removed modules | Update layout table from current tree |
|
|
66
|
+
| Config sample shows stale defaults | Copy from repo config template after confirming code |
|
|
67
|
+
| Doc lists a feature not in manifest/config | Remove or mark as external/optional |
|
|
68
|
+
| Duplicate facts disagree across two docs | Pick one canonical source; link from the other |
|
|
69
|
+
| Doc, manifest, and CI disagree on same command | Apply precedence (CI > manifest > source); report conflict if still ambiguous |
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Drift verification
|
|
2
|
+
|
|
3
|
+
**Load when:** Phase 4 — after any patch, or when reporting "docs already in sync."
|
|
4
|
+
|
|
5
|
+
**Do NOT load when:** Recon-only pass with zero edits planned — skip to a short "no drift found" note instead.
|
|
6
|
+
|
|
7
|
+
**Truth conflicts:** When sources disagree, apply **CI > manifest scripts/targets > source comments**. Report unresolved conflicts in **Gaps** — do not patch until the user picks a winner.
|
|
8
|
+
|
|
9
|
+
## Checklist
|
|
10
|
+
|
|
11
|
+
Mark each item that applies to scope. All applicable items must pass before "done."
|
|
12
|
+
|
|
13
|
+
### Always (any patch)
|
|
14
|
+
|
|
15
|
+
- [ ] Every edited claim traces to a truth file read this session (path named in report)
|
|
16
|
+
- [ ] No code or config files changed unless the user explicitly asked to fix defaults
|
|
17
|
+
- [ ] Diff stayed within scoped doc files — no cosmetic-only edits elsewhere
|
|
18
|
+
|
|
19
|
+
### Full sync
|
|
20
|
+
|
|
21
|
+
- [ ] README commands match manifest scripts/targets (or language equivalent); in monorepos, root README uses root manifest — package READMEs use package manifest only
|
|
22
|
+
- [ ] Config template in docs matches repo config defaults
|
|
23
|
+
- [ ] Path references in edited docs still exist on disk
|
|
24
|
+
- [ ] Duplicate facts across docs agree — one canonical source preferred
|
|
25
|
+
|
|
26
|
+
### Optional (when root instruction files were in scope)
|
|
27
|
+
|
|
28
|
+
- [ ] Command and layout tables match manifest, CI, and current tree
|
|
29
|
+
|
|
30
|
+
### Post-ship: dependency / manifest
|
|
31
|
+
|
|
32
|
+
- [ ] New manifest entries have matching README mention when user-facing
|
|
33
|
+
- [ ] Opt-in or experimental features documented when defaults are off
|
|
34
|
+
|
|
35
|
+
## Commands (adapt to project)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Which doc files changed
|
|
39
|
+
git diff --name-only -- '*.md'
|
|
40
|
+
|
|
41
|
+
# Node: list scripts
|
|
42
|
+
node -e "console.log(require('./package.json').scripts)"
|
|
43
|
+
|
|
44
|
+
# Rust: list bin targets
|
|
45
|
+
cargo metadata --no-deps --format-version 1 | head
|
|
46
|
+
|
|
47
|
+
# Python: project scripts (when pyproject.toml exists)
|
|
48
|
+
python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb')).get('project',{}).get('scripts',{}))" 2>/dev/null || true
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Run the project's test/lint command when code changed in the same task (from README, CONTRIBUTING, CI config, or manifest scripts). Markdown-only sync does not require a full test run.
|
|
52
|
+
|
|
53
|
+
## Report blockers
|
|
54
|
+
|
|
55
|
+
If a checklist item fails, report **blocked** with:
|
|
56
|
+
|
|
57
|
+
- which item failed
|
|
58
|
+
- what truth vs doc say
|
|
59
|
+
- whether user decision is needed (e.g. intentional undocumented feature)
|
|
@@ -36,6 +36,8 @@ export type RegisterBundledExtensionsOptions = {
|
|
|
36
36
|
cwd?: string;
|
|
37
37
|
/** Precomputed skips (tests); defaults to scanning Pi settings. */
|
|
38
38
|
globalSkips?: GlobalBundledExtensionSkip[];
|
|
39
|
+
/** When false, only global Pi settings are scanned (trust-safe startup). */
|
|
40
|
+
includeProjectSettings?: boolean;
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
export type RegisterBundledExtensionsResult = {
|
|
@@ -48,7 +50,11 @@ export async function registerBundledExtensions(
|
|
|
48
50
|
options: RegisterBundledExtensionsOptions = {},
|
|
49
51
|
): Promise<RegisterBundledExtensionsResult> {
|
|
50
52
|
const globalSkips =
|
|
51
|
-
options.globalSkips ??
|
|
53
|
+
options.globalSkips ??
|
|
54
|
+
detectGlobalBundledExtensionSkips({
|
|
55
|
+
cwd: options.cwd,
|
|
56
|
+
includeProjectSettings: options.includeProjectSettings ?? true,
|
|
57
|
+
});
|
|
52
58
|
const skipById = new Map(globalSkips.map((skip) => [skip.id, skip] as const));
|
|
53
59
|
|
|
54
60
|
const enabledIds = new Set<BundledExtensionId>();
|
|
@@ -17,6 +17,8 @@ export type GlobalBundledExtensionSkip = {
|
|
|
17
17
|
export type CollectGlobalExtensionSourcesOptions = {
|
|
18
18
|
cwd?: string;
|
|
19
19
|
homedir?: string;
|
|
20
|
+
/** When false, skip project `.pi/settings.json` (Pi project trust gate). */
|
|
21
|
+
includeProjectSettings?: boolean;
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
function isLocalPackageEntry(entry: string): boolean {
|
|
@@ -105,6 +107,7 @@ export function collectInstalledPackageNamesFromPiSettings(
|
|
|
105
107
|
): Set<string> {
|
|
106
108
|
const home = options.homedir ?? process.env.HOME ?? process.env.USERPROFILE ?? os.homedir();
|
|
107
109
|
const cwd = options.cwd ?? process.cwd();
|
|
110
|
+
const includeProjectSettings = options.includeProjectSettings ?? true;
|
|
108
111
|
const names = new Set<string>();
|
|
109
112
|
|
|
110
113
|
const sources: Array<{ settingsPath: string; baseDir: string }> = [
|
|
@@ -112,9 +115,15 @@ export function collectInstalledPackageNamesFromPiSettings(
|
|
|
112
115
|
settingsPath: path.join(home, ".pi", "agent", "settings.json"),
|
|
113
116
|
baseDir: path.join(home, ".pi", "agent"),
|
|
114
117
|
},
|
|
115
|
-
{ settingsPath: path.join(cwd, PI_PROJECT_CONFIG_DIR, "settings.json"), baseDir: cwd },
|
|
116
118
|
];
|
|
117
119
|
|
|
120
|
+
if (includeProjectSettings) {
|
|
121
|
+
sources.push({
|
|
122
|
+
settingsPath: path.join(cwd, PI_PROJECT_CONFIG_DIR, "settings.json"),
|
|
123
|
+
baseDir: cwd,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
118
127
|
for (const { settingsPath, baseDir } of sources) {
|
|
119
128
|
for (const entry of readSettingsEntries(settingsPath)) {
|
|
120
129
|
const npmName = parseNpmPackageName(entry);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
ProjectTrustContext,
|
|
4
|
+
ProjectTrustEventResult,
|
|
5
|
+
} from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import type { ResolvedProjectTrust } from "../config/hotmilk.ts";
|
|
7
|
+
|
|
8
|
+
const HOTMILK_TRUST_PROMPT = (cwd: string): string =>
|
|
9
|
+
`Trust this project for hotmilk?\n\n${cwd}\n\nEnables: .pi/settings.json, project extensions, .agents/skills, gentle-ai files under .pi/. Decline keeps global hotmilk bundles only.`;
|
|
10
|
+
|
|
11
|
+
export function resolveProjectTrustDecision(
|
|
12
|
+
settings: ResolvedProjectTrust,
|
|
13
|
+
ctx: ProjectTrustContext,
|
|
14
|
+
cwd: string,
|
|
15
|
+
): Promise<ProjectTrustEventResult> {
|
|
16
|
+
switch (settings.mode) {
|
|
17
|
+
case "always":
|
|
18
|
+
return Promise.resolve({ trusted: "yes", remember: settings.remember });
|
|
19
|
+
case "never":
|
|
20
|
+
return Promise.resolve({ trusted: "no", remember: settings.remember });
|
|
21
|
+
case "prompt":
|
|
22
|
+
if (!ctx.hasUI) {
|
|
23
|
+
return Promise.resolve({ trusted: "undecided" });
|
|
24
|
+
}
|
|
25
|
+
return ctx.ui.confirm(HOTMILK_TRUST_PROMPT(cwd), "Trust project").then((trusted) => ({
|
|
26
|
+
trusted: trusted ? "yes" : "no",
|
|
27
|
+
remember: trusted ? settings.remember : false,
|
|
28
|
+
}));
|
|
29
|
+
default:
|
|
30
|
+
return Promise.resolve({ trusted: "undecided" });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function registerProjectTrustHandlers(
|
|
35
|
+
pi: ExtensionAPI,
|
|
36
|
+
settings: ResolvedProjectTrust,
|
|
37
|
+
): void {
|
|
38
|
+
pi.on("project_trust", async (event, ctx): Promise<ProjectTrustEventResult> => {
|
|
39
|
+
return resolveProjectTrustDecision(settings, ctx, event.cwd);
|
|
40
|
+
});
|
|
41
|
+
}
|
package/src/bootstrap/session.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { applyContextStackOnSessionStart } from "./context-stack.ts";
|
|
3
|
+
import { detectGlobalBundledExtensionSkips } from "./global-extension-sources.ts";
|
|
3
4
|
import { seedAgentMcpJsonIfMissing } from "../config/mcp.ts";
|
|
4
5
|
import { AGENT_HOTMILK_CONFIG_LABEL, seedHotmilkConfigIfMissing } from "../config/hotmilk.ts";
|
|
5
6
|
import type { HotmilkRuntime } from "../config/runtime.ts";
|
|
@@ -16,6 +17,23 @@ const HOTMILK_PARSE_ERROR_MESSAGE = (path: string, error: string): string =>
|
|
|
16
17
|
const MCP_SEEDED_MESSAGE = (path: string): string =>
|
|
17
18
|
`Created ${path} from hotmilk MCP template (add servers for pi-mcp-adapter; context-mode uses the extension bridge).`;
|
|
18
19
|
|
|
20
|
+
function formatProjectTrustSkipMessage(
|
|
21
|
+
skips: HotmilkRuntime["globalExtensionSkips"],
|
|
22
|
+
): string | undefined {
|
|
23
|
+
if (skips.length === 0) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const rows = skips.map((skip) => `${skip.id}: project ${skip.packageName}`).join("\n");
|
|
27
|
+
return `Project settings also provide bundled packages (run /reload to dedupe):\n${rows}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function detectProjectOnlyBundledSkips(cwd: string): HotmilkRuntime["globalExtensionSkips"] {
|
|
31
|
+
const globalOnly = detectGlobalBundledExtensionSkips({ cwd, includeProjectSettings: false });
|
|
32
|
+
const withProject = detectGlobalBundledExtensionSkips({ cwd, includeProjectSettings: true });
|
|
33
|
+
const globalIds = new Set(globalOnly.map((skip) => skip.id));
|
|
34
|
+
return withProject.filter((skip) => !globalIds.has(skip.id));
|
|
35
|
+
}
|
|
36
|
+
|
|
19
37
|
function formatGlobalExtensionSkipsMessage(
|
|
20
38
|
skips: HotmilkRuntime["globalExtensionSkips"],
|
|
21
39
|
): string | undefined {
|
|
@@ -43,7 +61,7 @@ export function registerSessionHandlers(pi: ExtensionAPI, runtime: HotmilkRuntim
|
|
|
43
61
|
uiNotify(HOTMILK_PARSE_ERROR_MESSAGE(runtime.configPath, runtime.configError), "warning");
|
|
44
62
|
}
|
|
45
63
|
|
|
46
|
-
if (runtime.extensionToggles["gentle-ai"]) {
|
|
64
|
+
if (runtime.extensionToggles["gentle-ai"] && ctx.isProjectTrusted()) {
|
|
47
65
|
seedPersonaFromDefaults(ctx.cwd, runtime.defaults);
|
|
48
66
|
}
|
|
49
67
|
|
|
@@ -54,6 +72,15 @@ export function registerSessionHandlers(pi: ExtensionAPI, runtime: HotmilkRuntim
|
|
|
54
72
|
uiNotify(globalSkipMessage, "info");
|
|
55
73
|
}
|
|
56
74
|
|
|
75
|
+
if (ctx.isProjectTrusted()) {
|
|
76
|
+
const projectSkipMessage = formatProjectTrustSkipMessage(
|
|
77
|
+
detectProjectOnlyBundledSkips(ctx.cwd),
|
|
78
|
+
);
|
|
79
|
+
if (projectSkipMessage) {
|
|
80
|
+
uiNotify(projectSkipMessage, "warning");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
57
84
|
if (shouldWarnCavemanJaConflict(runtime.extensionToggles.caveman, runtime.defaults.language)) {
|
|
58
85
|
uiNotify(CAVEMAN_JA_CONFLICT_MESSAGE, "warning");
|
|
59
86
|
}
|
|
@@ -128,6 +128,12 @@ export const BUNDLED_EXTENSION_DEFINITIONS = [
|
|
|
128
128
|
module: "@tomxprime/planning-with-files/extensions/planning-with-files/index.ts",
|
|
129
129
|
group: "Workflow",
|
|
130
130
|
},
|
|
131
|
+
{
|
|
132
|
+
id: "plannotator",
|
|
133
|
+
package: { packageName: "@plannotator/pi-extension" },
|
|
134
|
+
module: "@plannotator/pi-extension/index.ts",
|
|
135
|
+
group: "Workflow",
|
|
136
|
+
},
|
|
131
137
|
{
|
|
132
138
|
id: "caveman",
|
|
133
139
|
package: { packageName: "pi-caveman" },
|
|
@@ -140,6 +146,12 @@ export const BUNDLED_EXTENSION_DEFINITIONS = [
|
|
|
140
146
|
module: "pi-red-green/dist/index.js",
|
|
141
147
|
group: "Workflow",
|
|
142
148
|
},
|
|
149
|
+
{
|
|
150
|
+
id: "autoresearch",
|
|
151
|
+
package: { packageName: "pi-autoresearch" },
|
|
152
|
+
module: "pi-autoresearch/extensions/pi-autoresearch/index.ts",
|
|
153
|
+
group: "Experiments",
|
|
154
|
+
},
|
|
143
155
|
{
|
|
144
156
|
id: "agent-dashboard",
|
|
145
157
|
package: {
|
package/src/config/hotmilk.ts
CHANGED
|
@@ -16,11 +16,16 @@ function readBundledHotmilkTemplate(): HotmilkConfig {
|
|
|
16
16
|
return JSON.parse(readFileSync(BUNDLED_TEMPLATE_PATH, "utf8")) as HotmilkConfig;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
function isProjectTrustMode(value: unknown): value is ProjectTrustMode {
|
|
20
|
+
return value === "delegate" || value === "prompt" || value === "always" || value === "never";
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
function buildDefaultConfigFromTemplate(template: HotmilkConfig): {
|
|
20
24
|
extensions: Record<BundledExtensionId, boolean>;
|
|
21
25
|
graph: ResolvedGraphSettings;
|
|
22
26
|
defaults: ResolvedDefaults;
|
|
23
27
|
mcp: ResolvedMcpSettings;
|
|
28
|
+
projectTrust: ResolvedProjectTrust;
|
|
24
29
|
} {
|
|
25
30
|
const extensions = {} as Record<BundledExtensionId, boolean>;
|
|
26
31
|
for (const id of BUNDLED_EXTENSION_IDS) {
|
|
@@ -33,6 +38,7 @@ function buildDefaultConfigFromTemplate(template: HotmilkConfig): {
|
|
|
33
38
|
|
|
34
39
|
const language = template.defaults?.language?.trim();
|
|
35
40
|
const persona = template.defaults?.persona;
|
|
41
|
+
const projectTrustMode = template.projectTrust?.mode;
|
|
36
42
|
|
|
37
43
|
return {
|
|
38
44
|
extensions,
|
|
@@ -47,6 +53,10 @@ function buildDefaultConfigFromTemplate(template: HotmilkConfig): {
|
|
|
47
53
|
mcp: {
|
|
48
54
|
seedOnStart: template.mcp?.seedOnStart ?? false,
|
|
49
55
|
},
|
|
56
|
+
projectTrust: {
|
|
57
|
+
mode: isProjectTrustMode(projectTrustMode) ? projectTrustMode : "delegate",
|
|
58
|
+
remember: template.projectTrust?.remember ?? false,
|
|
59
|
+
},
|
|
50
60
|
};
|
|
51
61
|
}
|
|
52
62
|
|
|
@@ -57,6 +67,13 @@ export { BUNDLED_EXTENSION_IDS, type BundledExtensionId } from "./bundled-extens
|
|
|
57
67
|
|
|
58
68
|
export type PersonaMode = "gentleman" | "neutral";
|
|
59
69
|
|
|
70
|
+
export type ProjectTrustMode = "delegate" | "prompt" | "always" | "never";
|
|
71
|
+
|
|
72
|
+
export type ResolvedProjectTrust = {
|
|
73
|
+
mode: ProjectTrustMode;
|
|
74
|
+
remember: boolean;
|
|
75
|
+
};
|
|
76
|
+
|
|
60
77
|
export type HotmilkConfig = {
|
|
61
78
|
extensions?: Partial<Record<BundledExtensionId, boolean>>;
|
|
62
79
|
graph?: {
|
|
@@ -70,6 +87,10 @@ export type HotmilkConfig = {
|
|
|
70
87
|
mcp?: {
|
|
71
88
|
seedOnStart?: boolean;
|
|
72
89
|
};
|
|
90
|
+
projectTrust?: {
|
|
91
|
+
mode?: ProjectTrustMode;
|
|
92
|
+
remember?: boolean;
|
|
93
|
+
};
|
|
73
94
|
};
|
|
74
95
|
|
|
75
96
|
type ConfigPathResult = {
|
|
@@ -174,4 +195,5 @@ export {
|
|
|
174
195
|
resolveDefaults,
|
|
175
196
|
resolveGraphSettings,
|
|
176
197
|
resolveMcpSettings,
|
|
198
|
+
resolveProjectTrust,
|
|
177
199
|
} from "./resolve.ts";
|
package/src/config/resolve.ts
CHANGED
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
type BundledExtensionId,
|
|
5
5
|
type HotmilkConfig,
|
|
6
6
|
type PersonaMode,
|
|
7
|
+
type ProjectTrustMode,
|
|
7
8
|
type ResolvedDefaults,
|
|
8
9
|
type ResolvedGraphSettings,
|
|
9
10
|
type ResolvedMcpSettings,
|
|
11
|
+
type ResolvedProjectTrust,
|
|
10
12
|
} from "./hotmilk.ts";
|
|
11
13
|
|
|
12
14
|
function isPersonaMode(value: unknown): value is PersonaMode {
|
|
@@ -46,3 +48,17 @@ export function resolveMcpSettings(config: HotmilkConfig): ResolvedMcpSettings {
|
|
|
46
48
|
seedOnStart: config.mcp?.seedOnStart ?? DEFAULT_HOTMILK_CONFIG.mcp.seedOnStart,
|
|
47
49
|
};
|
|
48
50
|
}
|
|
51
|
+
|
|
52
|
+
function isProjectTrustMode(value: unknown): value is ProjectTrustMode {
|
|
53
|
+
return value === "delegate" || value === "prompt" || value === "always" || value === "never";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function resolveProjectTrust(config: HotmilkConfig): ResolvedProjectTrust {
|
|
57
|
+
const mode = config.projectTrust?.mode;
|
|
58
|
+
const remember = config.projectTrust?.remember;
|
|
59
|
+
return {
|
|
60
|
+
mode: isProjectTrustMode(mode) ? mode : DEFAULT_HOTMILK_CONFIG.projectTrust.mode,
|
|
61
|
+
remember:
|
|
62
|
+
typeof remember === "boolean" ? remember : DEFAULT_HOTMILK_CONFIG.projectTrust.remember,
|
|
63
|
+
};
|
|
64
|
+
}
|
package/src/config/runtime.ts
CHANGED
|
@@ -6,12 +6,14 @@ import {
|
|
|
6
6
|
type ResolvedDefaults,
|
|
7
7
|
type ResolvedGraphSettings,
|
|
8
8
|
type ResolvedMcpSettings,
|
|
9
|
+
type ResolvedProjectTrust,
|
|
9
10
|
} from "./hotmilk.ts";
|
|
10
11
|
import {
|
|
11
12
|
resolveBundledExtensionToggles,
|
|
12
13
|
resolveDefaults,
|
|
13
14
|
resolveGraphSettings,
|
|
14
15
|
resolveMcpSettings,
|
|
16
|
+
resolveProjectTrust,
|
|
15
17
|
} from "./resolve.ts";
|
|
16
18
|
|
|
17
19
|
export type HotmilkRuntime = {
|
|
@@ -24,6 +26,7 @@ export type HotmilkRuntime = {
|
|
|
24
26
|
defaults: ResolvedDefaults;
|
|
25
27
|
graph: ResolvedGraphSettings;
|
|
26
28
|
mcp: ResolvedMcpSettings;
|
|
29
|
+
projectTrust: ResolvedProjectTrust;
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
export function createHotmilkRuntime(configRoot?: string): HotmilkRuntime {
|
|
@@ -37,5 +40,6 @@ export function createHotmilkRuntime(configRoot?: string): HotmilkRuntime {
|
|
|
37
40
|
defaults: resolveDefaults(loaded.config),
|
|
38
41
|
graph: resolveGraphSettings(loaded.config),
|
|
39
42
|
mcp: resolveMcpSettings(loaded.config),
|
|
43
|
+
projectTrust: resolveProjectTrust(loaded.config),
|
|
40
44
|
};
|
|
41
45
|
}
|
package/src/index.ts
CHANGED
|
@@ -12,11 +12,14 @@ import { registerSessionHandlers } from "./bootstrap/session.ts";
|
|
|
12
12
|
import { registerInputCommands, routeInputCommand } from "./controller/input.ts";
|
|
13
13
|
import { registerHotmilkSessionLogo } from "./ui/session-logo.ts";
|
|
14
14
|
import { installHotmilkCtxSearchCapture } from "./bootstrap/btw.ts";
|
|
15
|
+
import { registerProjectTrustHandlers } from "./bootstrap/project-trust.ts";
|
|
15
16
|
import { registerSubagentsDoctorCommand } from "./bootstrap/subagents-doctor.ts";
|
|
16
17
|
|
|
17
18
|
export default async function registerHotmilk(pi: ExtensionAPI): Promise<void> {
|
|
18
19
|
const runtime = createHotmilkRuntime();
|
|
19
20
|
|
|
21
|
+
registerProjectTrustHandlers(pi, runtime.projectTrust);
|
|
22
|
+
|
|
20
23
|
// Register before bundled imports so session_start handlers exist when bindExtensions emits.
|
|
21
24
|
registerHotmilkSessionLogo(pi);
|
|
22
25
|
installHotmilkCtxSearchCapture(pi);
|
|
@@ -25,6 +28,7 @@ export default async function registerHotmilk(pi: ExtensionAPI): Promise<void> {
|
|
|
25
28
|
|
|
26
29
|
const bundled = await registerBundledExtensions(pi, runtime.extensionToggles, {
|
|
27
30
|
cwd: process.cwd(),
|
|
31
|
+
includeProjectSettings: false,
|
|
28
32
|
});
|
|
29
33
|
if (runtime.extensionToggles.subagents === true) {
|
|
30
34
|
await registerSubagentsDoctorCommand(pi);
|
package/themes/monokai.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://raw.githubusercontent.com/
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
|
3
3
|
"name": "monokai",
|
|
4
4
|
"vars": {
|
|
5
5
|
"bg": "#272822",
|