@comento/pota-harness 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/workflows/pota-doctor.md +16 -0
- package/.agents/workflows/pota-install.md +15 -0
- package/.agents/workflows/pota-memory.md +17 -0
- package/.agents/workflows/pota-plan.md +22 -0
- package/.agents/workflows/pota-review.md +16 -0
- package/.agents/workflows/pota-toolcheck.md +9 -0
- package/.agents/workflows/pota-uninstall.md +9 -0
- package/.agents/workflows/pota-upgrade.md +15 -0
- package/.agents/workflows/pota-work.md +24 -0
- package/.claude/agents/explorer.md +7 -0
- package/.claude/agents/worker.md +7 -0
- package/.claude/commands/pota-doctor.md +5 -0
- package/.claude/commands/pota-install.md +9 -0
- package/.claude/commands/pota-memory.md +5 -0
- package/.claude/commands/pota-plan.md +5 -0
- package/.claude/commands/pota-review.md +5 -0
- package/.claude/commands/pota-toolcheck.md +7 -0
- package/.claude/commands/pota-uninstall.md +5 -0
- package/.claude/commands/pota-upgrade.md +9 -0
- package/.claude/commands/pota-work.md +7 -0
- package/.codex-plugin/plugin.json +33 -0
- package/LICENSE +21 -0
- package/README.de.md +166 -0
- package/README.es.md +166 -0
- package/README.fr.md +166 -0
- package/README.ja.md +166 -0
- package/README.ko.md +184 -0
- package/README.md +191 -2
- package/README.pt.md +166 -0
- package/README.zh.md +166 -0
- package/agents/explorer.md +36 -0
- package/agents/worker.md +45 -0
- package/commands/pota-doctor.md +28 -0
- package/commands/pota-install.md +29 -0
- package/commands/pota-memory.md +17 -0
- package/commands/pota-plan.md +22 -0
- package/commands/pota-review.md +16 -0
- package/commands/pota-toolcheck.md +17 -0
- package/commands/pota-uninstall.md +27 -0
- package/commands/pota-upgrade.md +26 -0
- package/commands/pota-work.md +24 -0
- package/docs/model-guidance/README.md +67 -0
- package/docs/model-guidance/claude.md +49 -0
- package/docs/model-guidance/codex.md +68 -0
- package/hooks/hooks.json +65 -0
- package/package.json +47 -4
- package/scripts/pota +14 -0
- package/scripts/pota-auto-upgrade.mjs +342 -0
- package/scripts/pota-cleanup.mjs +194 -0
- package/scripts/pota-goal-cli.mjs +337 -0
- package/scripts/pota-goal-evidence.mjs +186 -0
- package/scripts/pota-goal-history.mjs +316 -0
- package/scripts/pota-goal-operations-evidence.mjs +213 -0
- package/scripts/pota-goal-operations-mutations.mjs +97 -0
- package/scripts/pota-goal-operations.mjs +281 -0
- package/scripts/pota-goal-policy.mjs +62 -0
- package/scripts/pota-goal-state-validation.mjs +361 -0
- package/scripts/pota-goal-state.mjs +136 -0
- package/scripts/pota-goal.mjs +69 -0
- package/scripts/pota-index.mjs +1052 -0
- package/scripts/pota-lifecycle-claude.mjs +173 -0
- package/scripts/pota-lifecycle-commands.mjs +418 -0
- package/scripts/pota-lifecycle-config.mjs +153 -0
- package/scripts/pota-lifecycle-hosts.mjs +98 -0
- package/scripts/pota-lifecycle-integrity.mjs +272 -0
- package/scripts/pota-lifecycle-layout.mjs +52 -0
- package/scripts/pota-lifecycle-lock.mjs +205 -0
- package/scripts/pota-lifecycle-mirror.mjs +83 -0
- package/scripts/pota-lifecycle-staging.mjs +156 -0
- package/scripts/pota-lifecycle-transaction-journal.mjs +244 -0
- package/scripts/pota-lifecycle-transaction-targets.mjs +119 -0
- package/scripts/pota-lifecycle-transaction.mjs +12 -0
- package/scripts/pota-lifecycle.mjs +244 -0
- package/scripts/pota-paths.mjs +190 -0
- package/scripts/pota-safe-fs.mjs +277 -0
- package/scripts/pota-state-context.mjs +281 -0
- package/scripts/pota-state-hook.mjs +26 -0
- package/scripts/pota-state-init.mjs +80 -0
- package/scripts/pota-state.mjs +245 -0
- package/scripts/pota-toolcheck-detect.mjs +229 -0
- package/scripts/pota-toolcheck-runtime.mjs +178 -0
- package/scripts/pota-toolcheck.mjs +221 -0
- package/scripts/pota-update-state.mjs +219 -0
- package/scripts/pota.mjs +200 -0
- package/skills/pota-memory/SKILL.md +52 -0
- package/skills/pota-plan/SKILL.md +66 -0
- package/skills/pota-plan/delegation.md +65 -0
- package/skills/pota-plan/heavy-path.md +28 -0
- package/skills/pota-plan/planning-rules.md +78 -0
- package/skills/pota-review/SKILL.md +56 -0
- package/skills/pota-work/SKILL.md +71 -0
- package/skills/pota-work/delegation.md +65 -0
- package/skills/pota-work/heavy-path.md +18 -0
- package/skills/pota-work/recovery.md +29 -0
- package/skills/pota-work/verification.md +79 -0
- package/templates/MEMORY.md +12 -0
- package/templates/PLAN.md +9 -0
- package/templates/domain-memory.md +22 -0
- package/templates/work-plan.md +177 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { spawn as spawnProcess } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { lstat, rm, rmdir } from "node:fs/promises";
|
|
5
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
|
+
import { potaHome, installManifestPath, installRoot } from "./pota-paths.mjs";
|
|
7
|
+
|
|
8
|
+
// Auto-upgrade state shared by the SessionStart hook and the detached runner. The hook only reads small files here,
|
|
9
|
+
// claims a pending notice, and spawns the runner; it never touches the network, a lock, or the installation.
|
|
10
|
+
export const UPDATE_PACKAGE_NAME = "@comento/pota-harness";
|
|
11
|
+
export const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
12
|
+
export const NOTICE_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1000;
|
|
13
|
+
export const NOTICE_KINDS = ["updated", "failed", "manual"];
|
|
14
|
+
// Exit codes of `pota-lifecycle.mjs upgrade --auto` besides 0 (done) and 1 (anything else). They are a contract
|
|
15
|
+
// between versions: the runner is the installed version and the lifecycle it starts is the new one.
|
|
16
|
+
// `manual`: the files are committed and only the Claude Code step (cache refresh, registration, a missing claude CLI)
|
|
17
|
+
// is left to the user.
|
|
18
|
+
export const AUTO_UPGRADE_EXIT = Object.freeze({ manual: 3 });
|
|
19
|
+
const UPDATE_SCHEMA_VERSION = 1;
|
|
20
|
+
const SMALL_FILE_LIMIT = 64 * 1024;
|
|
21
|
+
const MANIFEST_FILE_LIMIT = 1024 * 1024;
|
|
22
|
+
const OFF_VALUES = new Set(["0", "false", "off", "no"]);
|
|
23
|
+
const INSTALLED_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
24
|
+
|
|
25
|
+
export function updatePaths(env = process.env) {
|
|
26
|
+
const home = potaHome(env);
|
|
27
|
+
const root = join(home, "update");
|
|
28
|
+
return {
|
|
29
|
+
home,
|
|
30
|
+
root,
|
|
31
|
+
config: join(home, "config.json"),
|
|
32
|
+
state: join(root, "state.json"),
|
|
33
|
+
notice: join(root, "notice.json"),
|
|
34
|
+
log: join(root, "last-run.log"),
|
|
35
|
+
lock: join(root, "update.lock"),
|
|
36
|
+
stage: join(root, "stage"),
|
|
37
|
+
manifest: installManifestPath(env),
|
|
38
|
+
runtimeParent: join(installRoot(env), "runtime"),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// A regular file at most `limit` bytes, parsed as JSON. Symlinks, directories, oversized and malformed files are
|
|
43
|
+
// "invalid"; only a missing file is "missing".
|
|
44
|
+
export function readJsonFile(path, limit = SMALL_FILE_LIMIT) {
|
|
45
|
+
let info;
|
|
46
|
+
try { info = lstatSync(path); } catch (error) { return { status: error?.code === "ENOENT" ? "missing" : "invalid" }; }
|
|
47
|
+
if (!info.isFile() || info.size > limit) return { status: "invalid" };
|
|
48
|
+
try { return { status: "ok", value: JSON.parse(readFileSync(path, "utf8")) }; } catch { return { status: "invalid" }; }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function writeJsonAtomic(path, value) {
|
|
52
|
+
writeFileAtomic(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function writeFileAtomic(path, text) {
|
|
56
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
57
|
+
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
58
|
+
try {
|
|
59
|
+
writeFileSync(temporary, text, { flag: "wx" });
|
|
60
|
+
renameSync(temporary, path);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
rmSync(temporary, { force: true });
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// On by default. POTA_AUTO_UPGRADE=0|false|off|no or config.json `autoUpgrade: false` turns it off, and so does a
|
|
68
|
+
// config.json that cannot be read or has the wrong shape: an unclear setting never starts an install.
|
|
69
|
+
export function autoUpgradeSetting(env = process.env, paths = updatePaths(env)) {
|
|
70
|
+
const raw = env.POTA_AUTO_UPGRADE;
|
|
71
|
+
if (typeof raw === "string" && OFF_VALUES.has(raw.trim().toLowerCase())) return { enabled: false, source: "env" };
|
|
72
|
+
const config = readJsonFile(paths.config);
|
|
73
|
+
if (config.status === "missing") return { enabled: true, source: "default" };
|
|
74
|
+
if (config.status !== "ok" || !isPlainObject(config.value)) return { enabled: false, source: "invalid-config" };
|
|
75
|
+
const value = config.value.autoUpgrade;
|
|
76
|
+
if (value === undefined) return { enabled: true, source: "default" };
|
|
77
|
+
if (typeof value !== "boolean") return { enabled: false, source: "invalid-config" };
|
|
78
|
+
return { enabled: value, source: "config" };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function readInstallManifest(paths) {
|
|
82
|
+
const read = readJsonFile(paths.manifest, MANIFEST_FILE_LIMIT);
|
|
83
|
+
const manifest = read.status === "ok" ? read.value : null;
|
|
84
|
+
if (manifest?.schemaVersion !== 2 || manifest.plugin !== "pota" || typeof manifest.version !== "string"
|
|
85
|
+
|| !INSTALLED_VERSION.test(manifest.version) || !["checkout", "package"].includes(manifest.origin)) return null;
|
|
86
|
+
return { version: manifest.version, installedAt: manifest.installedAt, origin: manifest.origin };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function readUpdateState(paths) {
|
|
90
|
+
const read = readJsonFile(paths.state);
|
|
91
|
+
return read.status === "ok" && isPlainObject(read.value) && read.value.schemaVersion === UPDATE_SCHEMA_VERSION ? read.value : null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function readPendingNotice(paths) {
|
|
95
|
+
const read = readJsonFile(paths.notice);
|
|
96
|
+
return read.status === "ok" && validNotice(read.value) ? read.value : null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function emptyUpdateState() {
|
|
100
|
+
return {
|
|
101
|
+
schemaVersion: UPDATE_SCHEMA_VERSION, lastCheckedAt: null, latestSeen: null, consecutiveFailures: 0,
|
|
102
|
+
nextAttemptAt: null, failedVersion: null, lastResult: null,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function noticeRecord(kind, from, to, at) {
|
|
107
|
+
return { schemaVersion: UPDATE_SCHEMA_VERSION, kind, from, to, at };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// The hook runs from an installed runtime only when its own .pota-runtime.json points at <install>/runtime/<version>.
|
|
111
|
+
// A source checkout has no locator there, so it reads and writes nothing else.
|
|
112
|
+
export function runningInstall(pluginRoot, paths) {
|
|
113
|
+
if (typeof pluginRoot !== "string") return null;
|
|
114
|
+
const locator = readJsonFile(join(pluginRoot, ".pota-runtime.json"));
|
|
115
|
+
if (locator.status !== "ok" || locator.value?.plugin !== "pota" || typeof locator.value.pluginRoot !== "string") return null;
|
|
116
|
+
const version = childName(paths.runtimeParent, locator.value.pluginRoot);
|
|
117
|
+
if (!version || !INSTALLED_VERSION.test(version)) return null;
|
|
118
|
+
return { runtimeRoot: join(paths.runtimeParent, version), version };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Due once a day after the later of the last check and the install; a recorded backoff can only push it further out.
|
|
122
|
+
export function checkDue({ state, manifest, now }) {
|
|
123
|
+
const time = now.getTime();
|
|
124
|
+
const next = Date.parse(state?.nextAttemptAt ?? "");
|
|
125
|
+
if (Number.isFinite(next) && time < next) return false;
|
|
126
|
+
const since = Math.max(...[state?.lastCheckedAt, manifest?.installedAt].map((value) => Date.parse(value ?? "")).filter(Number.isFinite));
|
|
127
|
+
return !Number.isFinite(since) || time - since >= CHECK_INTERVAL_MS;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// SessionStart: claim at most one pending notice, then start the runner when a check is due. Returns the claimed
|
|
131
|
+
// notice or null. Callers swallow errors so the capsule and exit code never depend on this step.
|
|
132
|
+
export function sessionStartUpdate({ env = process.env, now = new Date(), spawn = spawnProcess, pluginRoot } = {}) {
|
|
133
|
+
const paths = updatePaths(env);
|
|
134
|
+
const running = runningInstall(pluginRoot, paths);
|
|
135
|
+
if (!running) return null;
|
|
136
|
+
let notice = null;
|
|
137
|
+
try { notice = claimNotice(paths, { runningVersion: running.version, now }); } catch { notice = null; }
|
|
138
|
+
try { startRunnerWhenDue({ env, now, spawn, paths, running }); } catch { /* the next session tries again */ }
|
|
139
|
+
return notice;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function startRunnerWhenDue({ env, now, spawn, paths, running }) {
|
|
143
|
+
if (!autoUpgradeSetting(env, paths).enabled) return;
|
|
144
|
+
const manifest = readInstallManifest(paths);
|
|
145
|
+
// Only the canonical runtime of the installed version starts a runner; a retained runtime or a checkout install never does.
|
|
146
|
+
if (!manifest || manifest.origin !== "package" || manifest.version !== running.version) return;
|
|
147
|
+
if (!checkDue({ state: readUpdateState(paths), manifest, now })) return;
|
|
148
|
+
// stdio "ignore" keeps the hook's stdout pipe closed so the host does not wait for the runner; the error listener
|
|
149
|
+
// keeps a failed spawn from becoming an uncaught exception in the hook.
|
|
150
|
+
const child = spawn(process.execPath, [join(running.runtimeRoot, "scripts", "pota-auto-upgrade.mjs"), "run"], {
|
|
151
|
+
cwd: paths.home, env, detached: true, stdio: "ignore", windowsHide: true,
|
|
152
|
+
});
|
|
153
|
+
child?.on?.("error", () => {});
|
|
154
|
+
child?.unref?.();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// `updated` is shown only by a session that runs the new version; `failed` and `manual` by any installed version.
|
|
158
|
+
// The first session to rename the file shows it; notices older than 14 days are dropped silently.
|
|
159
|
+
export function claimNotice(paths, { runningVersion, now = new Date() }) {
|
|
160
|
+
const read = readJsonFile(paths.notice);
|
|
161
|
+
if (read.status === "missing") return null;
|
|
162
|
+
if (read.status !== "ok" || !validNotice(read.value) || now.getTime() - Date.parse(read.value.at) > NOTICE_MAX_AGE_MS) {
|
|
163
|
+
rmSync(paths.notice, { force: true });
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
if (!noticeShownBy(read.value, runningVersion)) return null;
|
|
167
|
+
const claimed = `${paths.notice}.${randomUUID()}.claimed`;
|
|
168
|
+
try { renameSync(paths.notice, claimed); } catch { return null; }
|
|
169
|
+
try {
|
|
170
|
+
const value = readJsonFile(claimed).value;
|
|
171
|
+
if (validNotice(value) && noticeShownBy(value, runningVersion)) return value;
|
|
172
|
+
// A runner replaced the notice between the read and the rename; put the newer one back for its own session.
|
|
173
|
+
try { lstatSync(paths.notice); } catch { renameSync(claimed, paths.notice); }
|
|
174
|
+
return null;
|
|
175
|
+
} finally {
|
|
176
|
+
rmSync(claimed, { force: true });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function noticeShownBy(notice, runningVersion) {
|
|
181
|
+
return notice.kind !== "updated" || notice.to === runningVersion;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function validNotice(value) {
|
|
185
|
+
return isPlainObject(value) && value.schemaVersion === UPDATE_SCHEMA_VERSION && NOTICE_KINDS.includes(value.kind)
|
|
186
|
+
&& typeof value.to === "string" && typeof value.from === "string" && Number.isFinite(Date.parse(value.at ?? ""));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Called by uninstall after the lifecycle lock is released: the known update files and the runner's stage go, and the
|
|
190
|
+
// directory is removed when empty. config.json and projects/ stay. A running runner (its lock present) keeps its stage.
|
|
191
|
+
export async function removeUpdateState(env = process.env) {
|
|
192
|
+
const paths = updatePaths(env);
|
|
193
|
+
try {
|
|
194
|
+
if (!(await lstat(paths.root)).isDirectory()) return;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (error?.code === "ENOENT") return;
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
for (const path of [paths.state, paths.notice, paths.log]) await rm(path, { force: true });
|
|
200
|
+
let locked = true;
|
|
201
|
+
try { await lstat(paths.lock); } catch (error) { if (error?.code === "ENOENT") locked = false; else throw error; }
|
|
202
|
+
if (!locked) await rm(paths.stage, { recursive: true, force: true });
|
|
203
|
+
try { await rmdir(paths.root); } catch (error) { if (!["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error?.code)) throw error; }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function childName(parent, child) {
|
|
207
|
+
const direct = singleSegment(relative(resolve(parent), resolve(child)));
|
|
208
|
+
if (direct) return direct;
|
|
209
|
+
try { return singleSegment(relative(realpathSync(parent), realpathSync(child))); } catch { return null; }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function singleSegment(relation) {
|
|
213
|
+
if (!relation || relation === ".." || relation.startsWith(`..${sep}`) || isAbsolute(relation) || relation.includes(sep)) return null;
|
|
214
|
+
return relation;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function isPlainObject(value) {
|
|
218
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
219
|
+
}
|
package/scripts/pota.mjs
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { runState } from "./pota-state.mjs";
|
|
7
|
+
import { INDEX_USAGE, checkIndex, parseIndexArgs, updateIndex } from "./pota-index.mjs";
|
|
8
|
+
import { parseRootArgs, resolvePluginRoot } from "./pota-paths.mjs";
|
|
9
|
+
|
|
10
|
+
const pluginRoot = resolvePluginRoot(import.meta.url);
|
|
11
|
+
const scriptDir = join(pluginRoot, "scripts");
|
|
12
|
+
const version = JSON.parse(readFileSync(join(pluginRoot, "package.json"), "utf8")).version;
|
|
13
|
+
|
|
14
|
+
export async function main(command = "help", args = [], { runner = spawnSync } = {}) {
|
|
15
|
+
if (command === "help" || command === "--help") {
|
|
16
|
+
console.log(message("usage"));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (command === "version" || command === "--version") {
|
|
20
|
+
console.log(`pota ${version}`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (["install", "upgrade", "doctor", "uninstall"].includes(command)) {
|
|
24
|
+
const result = runner(process.execPath, [join(scriptDir, "pota-lifecycle.mjs"), command, ...args], { stdio: "inherit" });
|
|
25
|
+
process.exitCode = result.status ?? 1;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (["goal", "task"].includes(command)) {
|
|
29
|
+
const goalArgs = command === "goal" ? args : ["task", ...args];
|
|
30
|
+
const result = runner(process.execPath, [join(scriptDir, "pota-goal.mjs"), ...goalArgs], { stdio: "inherit" });
|
|
31
|
+
process.exitCode = result.status ?? 1;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (command === "memory" || command === "plan") {
|
|
35
|
+
const options = parseIndexArgs(command, args);
|
|
36
|
+
if (options.action === "guidance") {
|
|
37
|
+
console.log(message(command));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (options.action === "help") {
|
|
41
|
+
console.log(INDEX_USAGE[command]);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (options.action === "version") {
|
|
45
|
+
console.log(`pota ${version}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const report = options.action === "check"
|
|
49
|
+
? await checkIndex(options.root, command, { strict: options.strict })
|
|
50
|
+
: await updateIndex(options.root, command, { dryRun: options.dryRun, archive: options.archive, ko: localeFamily() === "ko" });
|
|
51
|
+
console.log(JSON.stringify(report, null, 2));
|
|
52
|
+
process.exitCode = report.ok ? 0 : 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const { args: rest, repoRoot } = parseRootArgs(args);
|
|
56
|
+
if (command === "init") {
|
|
57
|
+
// Reject every remaining argument before init writes anything.
|
|
58
|
+
if (rest.length === 1 && rest[0] === "--help") {
|
|
59
|
+
console.log("Usage: pota init [--root PATH]");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (rest.length === 1 && rest[0] === "--version") {
|
|
63
|
+
console.log(`pota ${version}`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const unexpected = rest.find((argument) => argument !== "--help" && argument !== "--version");
|
|
67
|
+
if (unexpected !== undefined) throw new Error(`unknown option: ${unexpected}`);
|
|
68
|
+
if (rest.includes("--help") && rest.includes("--version")) throw new Error("--help and --version cannot be used together");
|
|
69
|
+
if (rest.length > 0) throw new Error(`${rest[0]} may only be provided once`);
|
|
70
|
+
console.log(await runState("manual", { root: repoRoot }));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (command === "toolcheck") {
|
|
74
|
+
const result = runner(process.execPath, [join(scriptDir, "pota-toolcheck.mjs"), ...args], { stdio: "inherit" });
|
|
75
|
+
process.exitCode = result.status ?? 1;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (command === "auto-upgrade") {
|
|
79
|
+
const result = runner(process.execPath, [join(scriptDir, "pota-auto-upgrade.mjs"), ...args], { stdio: "inherit" });
|
|
80
|
+
process.exitCode = result.status ?? 1;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (command === "cleanup") {
|
|
84
|
+
const result = runner(process.execPath, [join(scriptDir, "pota-cleanup.mjs"), ...args], { stdio: "inherit" });
|
|
85
|
+
process.exitCode = result.status ?? 1;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (["work", "review"].includes(command)) {
|
|
89
|
+
console.log(message(command));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
throw new Error(`unknown command: ${command}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function message(key, locale = localeValue()) {
|
|
96
|
+
const family = localeFamily(locale);
|
|
97
|
+
return messages[family]?.[key] ?? messages.en[key];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function localeFamily(locale = localeValue()) {
|
|
101
|
+
const normalized = locale.toLowerCase();
|
|
102
|
+
for (const family of ["ko", "ja", "zh", "es", "fr", "de", "pt"]) {
|
|
103
|
+
if (normalized.startsWith(family)) return family;
|
|
104
|
+
}
|
|
105
|
+
return "en";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function localeValue() {
|
|
109
|
+
return [process.env.LC_ALL, process.env.LC_MESSAGES, process.env.LANG]
|
|
110
|
+
.find((value) => typeof value === "string" && value.length > 0) ?? Intl.DateTimeFormat().resolvedOptions().locale;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isCliEntry() {
|
|
114
|
+
if (!process.argv[1]) return false;
|
|
115
|
+
try {
|
|
116
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1]);
|
|
117
|
+
} catch {
|
|
118
|
+
return import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const planExecutionPolicy = "The user-called/main agent is the orchestrator; implementation edits go to worker subagents whenever subagent tools are available. When the subagent tool provides a progress-reporting channel, subagents report status to the orchestrator when starting work, when deciding or confirming direction, during periodic progress, and when finishing; the orchestrator must immediately relay received status events to the user. If no mid-run reporting channel exists, the orchestrator relays observable events such as assignment, waiting, and final completion. When a delegated subagent finishes, it must provide its final report before leaving; after the orchestrator captures the final report and evidence, the orchestrator must close or release the completed subagent, then review the final report and evidence before marking the work complete. If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence.";
|
|
123
|
+
const workExecutionPolicy = "The user-called/main agent orchestrates, while worker subagents perform implementation edits whenever subagent tools are available. When the subagent tool provides a progress-reporting channel, subagents report status to the orchestrator when starting work, when deciding or confirming direction, during periodic progress, and when finishing; the orchestrator must immediately relay received status events to the user. If no mid-run reporting channel exists, the orchestrator relays observable events such as assignment, waiting, and final completion. When a delegated subagent finishes, it must provide its final report before leaving; after the orchestrator captures the final report and evidence, the orchestrator must close or release the completed subagent, then review the final report and evidence before marking the work complete. If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence.";
|
|
124
|
+
const tokenEfficientWorkPolicy = "Use a test contract for requirements, invariants, boundaries, and failure modes before the minimum implementation. The tool exit code is authoritative. Run goal verify -- <argv> and expand only failures. Inspect package lifecycle scripts: content-producing or unknown scripts must never use --ignore-scripts; only absent or proven content-neutral scripts may use it while prior full-check evidence remains fresh.";
|
|
125
|
+
const stagedTriagePolicy = "Use request, planning, and code checkpoints. Before the first edit, evidence may change either route; synchronize the plan artifact, repository goal task roadmap through goal replan, and native UI plan. After editing starts, a new Heavy Path signal promotes Light Path to Heavy Path: stop further edits, mark affected evidence stale, synchronize all three roadmaps, and repeat the code checkpoint.";
|
|
126
|
+
const stagedTriagePolicyKo = "요청, 계획, 코드 체크포인트를 사용합니다. 첫 편집 전에는 증거에 따라 어느 경로로든 바꿀 수 있으며 plan artifact, 저장소 goal task roadmap, native UI plan을 동기화합니다. 편집 뒤 새 Heavy Path 신호가 나오면 Light Path를 Heavy Path로 승격하고 추가 편집을 중단하며 관련 증거를 stale로 표시한 뒤 세 roadmap과 코드 체크포인트를 다시 맞춥니다.";
|
|
127
|
+
const reasoningEffortPolicy = "Choose a model per task (fast/balanced/stronger/strongest) and pass it like effort; route reasoning effort by task: Light planning/implementation/verification=medium; Heavy planning/review/implementation=high; final verification/review=xhigh. Every new task/worker dispatch MUST pass the requested effort through the host option (Claude Code: Agent `effort`, never fork) and the chosen model the same way (Agent `model`); Effective is the passed value, `not-applied: <reason>` when not passed, `inherited` only when the host has no option. Never change global/user config.";
|
|
128
|
+
const artifactLocalePolicy = "Write user-visible responses and generated or updated documentation, plans, and memory artifacts in the OS locale unless the user asks for another language.";
|
|
129
|
+
const memoryIndexPolicy = "Each MEMORY.md line under Domain Knowledge locates one docs/memory/<name>.md note with its one-line Summary; maintain it with pota memory index and verify it with pota memory check.";
|
|
130
|
+
const planIndexPolicy = "Each PLAN.md line locates one docs/plan/<name>.md plan under Active Plans or Completed Plans (newest first); maintain it with pota plan index (--archive moves older entries to docs/plan/ARCHIVE.md) and verify it with pota plan check.";
|
|
131
|
+
const artifactLocalePolicyKo = "사용자가 다른 언어를 요청하지 않는 한, 사용자에게 보이는 응답과 생성 또는 갱신하는 문서, 계획, 메모리 산출물은 OS locale 언어로 작성합니다.";
|
|
132
|
+
|
|
133
|
+
const messages = {
|
|
134
|
+
en: {
|
|
135
|
+
memory: `Use the pota-memory skill. Keep MEMORY.md as a short index and write details to docs/memory/<domain>.md. ${artifactLocalePolicy} ${memoryIndexPolicy}`,
|
|
136
|
+
plan: `Use the pota-plan skill before non-trivial implementation. Read project-root MEMORY.md first when present; if absent, continue without repository memory. Understand the whole work first, then classify it into executable tasks and sub-tasks. Keep PLAN.md as an index, write detailed plans to docs/plan/<topic>.md, and record Light/Heavy Path triage before any mutation. ${stagedTriagePolicy} ${reasoningEffortPolicy} ${artifactLocalePolicy} ${planExecutionPolicy} ${planIndexPolicy}`,
|
|
137
|
+
work: `Use pota-work only after a plan exists. Read root MEMORY.md when present and continue without it when absent. ${tokenEfficientWorkPolicy} Execute one task through its recorded Light/Heavy Path. ${stagedTriagePolicy} ${reasoningEffortPolicy} ${workExecutionPolicy} ${artifactLocalePolicy} Resume active work after side questions unless asked to pause, stop, or switch.`,
|
|
138
|
+
review: `Use the pota-review skill. Compare evidence against PLAN.md and MEMORY.md. ${artifactLocalePolicy}`,
|
|
139
|
+
usage: "Usage: pota install|upgrade|doctor|uninstall|auto-upgrade|cleanup|init|memory|plan|work|review|toolcheck|goal|task",
|
|
140
|
+
},
|
|
141
|
+
ko: {
|
|
142
|
+
memory: `pota-memory 스킬을 사용하세요. MEMORY.md는 짧은 색인으로, 상세 내용은 docs/memory/<domain>.md에 기록합니다. ${artifactLocalePolicyKo} ${memoryIndexPolicy}`,
|
|
143
|
+
plan: `중요 구현 전에는 pota-plan 스킬을 사용하세요. 프로젝트 루트 MEMORY.md가 있으면 먼저 읽고, 없으면 저장소 메모리 없이 계속 진행합니다. 먼저 전체 작업을 파악한 뒤 실행 가능한 task와 필요한 sub-task로 분류합니다. PLAN.md는 색인으로, 상세 계획은 docs/plan/<topic>.md에 기록하며 파일 변경 전 Light/Heavy Path 트리아지를 남깁니다. ${stagedTriagePolicyKo} ${reasoningEffortPolicy} ${artifactLocalePolicyKo} ${planExecutionPolicy} ${planIndexPolicy}`,
|
|
144
|
+
work: `계획이 있으면 pota-work를 사용하세요. 루트 MEMORY.md가 있으면 읽고 없으면 무시하고 진행합니다. ${tokenEfficientWorkPolicy} 기록된 Light/Heavy Path로 한 task를 수행합니다. ${stagedTriagePolicyKo} ${reasoningEffortPolicy} ${workExecutionPolicy} ${artifactLocalePolicyKo} 곁가지 질문 뒤에는 일시중지·중단·전환 요청이 없으면 active work를 재개합니다.`,
|
|
145
|
+
review: `pota-review 스킬을 사용하세요. PLAN.md와 MEMORY.md 기준으로 증거를 비교합니다. ${artifactLocalePolicyKo}`,
|
|
146
|
+
usage: "사용법: pota install|upgrade|doctor|uninstall|auto-upgrade|cleanup|init|memory|plan|work|review|toolcheck|goal|task",
|
|
147
|
+
},
|
|
148
|
+
ja: {
|
|
149
|
+
memory: `pota-memory スキルを使用してください。MEMORY.md は短い索引にし、詳細は docs/memory/<domain>.md に記録します。${artifactLocalePolicy} ${memoryIndexPolicy}`,
|
|
150
|
+
plan: `pota-plan スキルを使用してください。PLAN.md は索引にし、詳細計画は docs/plan/<topic>.md に記録します。${reasoningEffortPolicy} ${artifactLocalePolicy} ${planExecutionPolicy} ${planIndexPolicy}`,
|
|
151
|
+
work: `pota-work スキルを使用してください。1つのモジュールスライスを実行し、モジュール証拠と表面証拠を取得します。${tokenEfficientWorkPolicy} ${reasoningEffortPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
|
|
152
|
+
review: `pota-review スキルを使用してください。PLAN.md と MEMORY.md に照らして証拠を比較します。${artifactLocalePolicy}`,
|
|
153
|
+
usage: "使い方: pota install|upgrade|doctor|uninstall|auto-upgrade|cleanup|init|memory|plan|work|review|toolcheck|goal|task",
|
|
154
|
+
},
|
|
155
|
+
zh: {
|
|
156
|
+
memory: `使用 pota-memory skill。将 MEMORY.md 保持为简短索引,并将详细内容写入 docs/memory/<domain>.md。${artifactLocalePolicy} ${memoryIndexPolicy}`,
|
|
157
|
+
plan: `使用 pota-plan skill。将 PLAN.md 保持为索引,并将详细计划写入 docs/plan/<topic>.md。${reasoningEffortPolicy} ${artifactLocalePolicy} ${planExecutionPolicy} ${planIndexPolicy}`,
|
|
158
|
+
work: `使用 pota-work skill。执行一个模块切片,并收集模块证据。${tokenEfficientWorkPolicy} ${reasoningEffortPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
|
|
159
|
+
review: `使用 pota-review skill。根据 PLAN.md 和 MEMORY.md 对比证据。${artifactLocalePolicy}`,
|
|
160
|
+
usage: "用法: pota install|upgrade|doctor|uninstall|auto-upgrade|cleanup|init|memory|plan|work|review|toolcheck|goal|task",
|
|
161
|
+
},
|
|
162
|
+
es: {
|
|
163
|
+
memory: `Usa la skill pota-memory. Mantén MEMORY.md como un índice breve y escribe los detalles en docs/memory/<domain>.md. ${artifactLocalePolicy} ${memoryIndexPolicy}`,
|
|
164
|
+
plan: `Usa la skill pota-plan. Mantén PLAN.md como índice y escribe los planes detallados en docs/plan/<topic>.md. ${reasoningEffortPolicy} ${artifactLocalePolicy} ${planExecutionPolicy} ${planIndexPolicy}`,
|
|
165
|
+
work: `Usa la skill pota-work. Ejecuta un solo segmento de módulo y captura evidencia de módulo y de superficie. ${tokenEfficientWorkPolicy} ${reasoningEffortPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
|
|
166
|
+
review: `Usa la skill pota-review. Compara la evidencia con PLAN.md y MEMORY.md. ${artifactLocalePolicy}`,
|
|
167
|
+
usage: "Uso: pota install|upgrade|doctor|uninstall|auto-upgrade|cleanup|init|memory|plan|work|review|toolcheck|goal|task",
|
|
168
|
+
},
|
|
169
|
+
fr: {
|
|
170
|
+
memory: `Utilisez la skill pota-memory. Gardez MEMORY.md comme index court et écrivez les détails dans docs/memory/<domain>.md. ${artifactLocalePolicy} ${memoryIndexPolicy}`,
|
|
171
|
+
plan: `Utilisez la skill pota-plan. Gardez MEMORY.md comme index et écrivez les plans détaillés dans docs/plan/<topic>.md. ${reasoningEffortPolicy} ${artifactLocalePolicy} ${planExecutionPolicy} ${planIndexPolicy}`,
|
|
172
|
+
work: `Utilisez la skill pota-work. Exécutez une tranche de module et capturez les preuves de module et de surface. ${tokenEfficientWorkPolicy} ${reasoningEffortPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
|
|
173
|
+
review: `Utilisez la skill pota-review. Comparez les preuves avec PLAN.md et MEMORY.md. ${artifactLocalePolicy}`,
|
|
174
|
+
usage: "Utilisation: pota install|upgrade|doctor|uninstall|auto-upgrade|cleanup|init|memory|plan|work|review|toolcheck|goal|task",
|
|
175
|
+
},
|
|
176
|
+
de: {
|
|
177
|
+
memory: `Verwenden Sie die Skill pota-memory. Halten Sie MEMORY.md als kurzen Index und schreiben Sie Details nach docs/memory/<domain>.md. ${artifactLocalePolicy} ${memoryIndexPolicy}`,
|
|
178
|
+
plan: `Verwenden Sie die Skill pota-plan. Halten Sie MEMORY.md als Index und schreiben Sie Detailpläne nach docs/plan/<topic>.md. ${reasoningEffortPolicy} ${artifactLocalePolicy} ${planExecutionPolicy} ${planIndexPolicy}`,
|
|
179
|
+
work: `Verwenden Sie die Skill pota-work. Führen Sie einen Modulabschnitt aus und erfassen Sie Modul- und Oberflächenbelege. ${tokenEfficientWorkPolicy} ${reasoningEffortPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
|
|
180
|
+
review: `Verwenden Sie die Skill pota-review. Vergleichen Sie Belege mit PLAN.md und MEMORY.md. ${artifactLocalePolicy}`,
|
|
181
|
+
usage: "Verwendung: pota install|upgrade|doctor|uninstall|auto-upgrade|cleanup|init|memory|plan|work|review|toolcheck|goal|task",
|
|
182
|
+
},
|
|
183
|
+
pt: {
|
|
184
|
+
memory: `Use a skill pota-memory. Mantenha MEMORY.md como um índice curto e escreva detalhes em docs/memory/<domain>.md. ${artifactLocalePolicy} ${memoryIndexPolicy}`,
|
|
185
|
+
plan: `Use a skill pota-plan. Mantenha MEMORY.md como índice e escreva planos detalhados em docs/plan/<topic>.md. ${reasoningEffortPolicy} ${artifactLocalePolicy} ${planExecutionPolicy} ${planIndexPolicy}`,
|
|
186
|
+
work: `Use a skill pota-work. Execute uma fatia de módulo e capture evidências de módulo e de superfície. ${tokenEfficientWorkPolicy} ${reasoningEffortPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
|
|
187
|
+
review: `Use a skill pota-review. Compare evidências com PLAN.md e MEMORY.md. ${artifactLocalePolicy}`,
|
|
188
|
+
usage: "Uso: pota install|upgrade|doctor|uninstall|auto-upgrade|cleanup|init|memory|plan|work|review|toolcheck|goal|task",
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
if (isCliEntry()) {
|
|
193
|
+
const [command = "help", ...args] = process.argv.slice(2);
|
|
194
|
+
try {
|
|
195
|
+
await main(command, args);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error(`Pota error: ${error instanceof Error ? error.message : String(error)}`);
|
|
198
|
+
process.exitCode = 2;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pota-memory
|
|
3
|
+
description: Create and maintain repository MEMORY.md and docs/memory domain notes for token-efficient Codex, Claude Code, and Antigravity agent work.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pota Memory
|
|
7
|
+
|
|
8
|
+
Use this when domain knowledge, repository policy, or pre-plan context compression is needed.
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
The root `MEMORY.md` is an index, not detailed knowledge. Record details in `docs/memory/<domain>.md`.
|
|
13
|
+
|
|
14
|
+
Every delegated agent reads project-root `MEMORY.md` when present and continues when absent before doing its assigned discovery, evidence, or update task.
|
|
15
|
+
|
|
16
|
+
When subagent tools are available, each agent may recursively delegate bounded sub-tasks to subagents. Every child subagent reads project-root `MEMORY.md` when present and continues when absent, keeps the assigned scope, and preserves others' edits.
|
|
17
|
+
|
|
18
|
+
## Runtime Location
|
|
19
|
+
|
|
20
|
+
Resolve Pota's read-only runtime from `references/pota-runtime.json` next to this `SKILL.md`. Its `pluginRoot` contains Pota's `scripts/`, `templates/`, commands, agents, and model guidance. In a source checkout where the locator does not exist, resolve the plugin root from this `SKILL.md` location (`../..`). Never look for Pota runtime files in the target repository or derive the plugin root from the current working directory. If the shared runtime or locator is missing, unreadable, or inconsistent, stop every dependent mirror and recover from the published/global package with `pota doctor` followed by `pota upgrade`; do not use another mirror as a repair source.
|
|
21
|
+
|
|
22
|
+
Keep the target repository root separate. Pass it explicitly with `--root <repoRoot>` when invoking the installed runtime. Repository artifacts include `MEMORY.md`, `PLAN.md`, `docs/memory/`, and `docs/plan/`; runtime goal state lives only under the user-home Pota directory.
|
|
23
|
+
|
|
24
|
+
## Procedure
|
|
25
|
+
|
|
26
|
+
1. Run `node "<pluginRoot>/scripts/pota.mjs" init --root "<repoRoot>"` through the resolved Pota runtime. If the runtime is unavailable, ensure `MEMORY.md`, `PLAN.md`, `docs/memory`, and `docs/plan` directly and report that `pota doctor` or `pota upgrade` is required.
|
|
27
|
+
2. Explore the repository before asking the user.
|
|
28
|
+
3. Delegate when it materially improves speed or quality instead of asking the user.
|
|
29
|
+
- `explorer`: read-only domain discovery, boundaries, invariants, policies, contradictions, and stale facts when available.
|
|
30
|
+
- `worker`: exact file paths, commands, schemas, references, and bounded evidence capture.
|
|
31
|
+
- Recursive subagent delegation is allowed only for bounded sub-tasks when the current surface supports it.
|
|
32
|
+
4. Write detailed facts to `docs/memory/<domain>.md` from `pota://templates/domain-memory.md`. Put a `Summary:` line under the H1: one line of at most 120 characters stating what the note answers and when to read it. The index and the session-start hook show that line, so agents can pick notes without opening them.
|
|
33
|
+
5. Keep only one-line entries in `MEMORY.md` under `## Domain Knowledge` (`- [<name>](docs/memory/<name>.md): <summary>`); a note link in another section is a mention that `index` never rewrites. Run `node "<pluginRoot>/scripts/pota.mjs" memory index --root "<repoRoot>"` to add unlisted notes and replace the summary after the link when `Summary:` changed (`--dry-run` previews; it removes only exact Pota placeholder lines in that section, never deletes entries or dead links, and writes nothing when the insertion point is inside an unclosed code fence), then `memory check --root "<repoRoot>"`: exit 1 lists unindexed notes, dead links, duplicates, stale summaries, and control characters to fix; warnings fail only with `--strict`.
|
|
34
|
+
6. When a lesson or correction recurs and is machine-checkable, encode it as a test, lint rule, script, or hook instead of more prose; memory keeps judgment rules and a pointer to the enforcing check.
|
|
35
|
+
7. Write user-visible responses and generated or updated documentation, plans, and memory artifacts in the OS locale unless the user asks for another language.
|
|
36
|
+
|
|
37
|
+
## Delegation Prompt Format
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
TASK: Explore persistent domain knowledge for <domain>.
|
|
41
|
+
EXPECTED OUTCOME: Return facts, source paths, and proof commands concisely.
|
|
42
|
+
REQUIRED TOOLS: Read-only file search, LSP/symbol tools, command checks.
|
|
43
|
+
MUST DO: Read the project-root `MEMORY.md` before doing assigned work. Preserve proper nouns exactly. Cite local paths. Separate facts from inference. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts.
|
|
44
|
+
MUST NOT DO: Edit files. Ask the user. Guess missing names.
|
|
45
|
+
CONTEXT: Current task, repository root, existing MEMORY.md entries.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Completion Criteria
|
|
49
|
+
|
|
50
|
+
- `MEMORY.md` links to detailed notes, one line each, and `memory check` exits 0.
|
|
51
|
+
- Each note starts with a `Summary:` line and has clear scope and sources, and is shorter than reading the original files wholesale.
|
|
52
|
+
- Long explanations are not duplicated in the root index.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pota-plan
|
|
3
|
+
description: Use before non-trivial implementation, refactoring, migrations, debugging, repository automation, or any request that asks to plan, design, implement carefully, triage complexity, or coordinate agents. Creates decision-complete PLAN.md and docs/plan plans through repository evidence and bounded delegation instead of user questions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pota Plan
|
|
7
|
+
|
|
8
|
+
Use before non-trivial implementation, refactoring, migrations, debugging, or repository automation, or when the user asks for planning, design, "설계부터", "신중하게", "꼼꼼히", or coordinated agents. Plans reduce execution tokens: store the topic in `PLAN.md` and details in `docs/plan/<topic>.md`, never only in chat.
|
|
9
|
+
|
|
10
|
+
## Re-entry and Required References
|
|
11
|
+
|
|
12
|
+
At the start and after compaction, restart, or handoff, restore context in this order: optional root `MEMORY.md` → `pota-plan` → the active plan → current-task references → model guidance recorded by the plan. Missing `MEMORY.md` is not an inconsistency: continue without repository memory and never invoke another memory service. If persisted state names a plan or task and that required reference is missing, unreadable, or inconsistent, do not explore, edit, delegate, or complete work; report or record a blocker first. No read-receipt fields: state consistency and bound tool evidence are the authority.
|
|
13
|
+
|
|
14
|
+
## Runtime Location
|
|
15
|
+
|
|
16
|
+
Resolve the read-only runtime from `references/pota-runtime.json` next to this `SKILL.md` (its `pluginRoot`; in a source checkout without it, this file's `../..`). Never search the target repository for Pota files or derive `pluginRoot` from the cwd. If the runtime or locator is missing, unreadable, or inconsistent, stop every dependent mirror and recover with published/global `pota doctor` followed by `pota upgrade`; never repair from another mirror.
|
|
17
|
+
|
|
18
|
+
Plans use logical IDs such as `pota://templates/work-plan.md`, resolved through `pluginRoot`. The session-start hook prints a ready prefix `Pota CLI: node "<abs>/scripts/pota.mjs" goal <command>`: copy it literally into every command and never store it in a shell variable (shell variables do not persist between tool calls). `--root` may be omitted when the cwd is inside the repository (it resolves via `HARNESS_REPO_ROOT`, else the nearest `.git` ancestor).
|
|
19
|
+
|
|
20
|
+
## Reference Files
|
|
21
|
+
|
|
22
|
+
Read a reference only when the selected path or situation requires it (Quick and Light Path never need `heavy-path.md`):
|
|
23
|
+
|
|
24
|
+
- `pota://skills/pota-plan/planning-rules.md`: plan rules, route record, tool readiness, dry-run policy; read when finalizing a plan.
|
|
25
|
+
- `pota://skills/pota-plan/delegation.md`: subagent lifecycle, prompt format, reasoning-effort routing; read before dispatching.
|
|
26
|
+
- `pota://skills/pota-plan/heavy-path.md`: Heavy Path flow, route changes, review gates; read only on a Heavy signal or route change.
|
|
27
|
+
|
|
28
|
+
## Path Selection
|
|
29
|
+
|
|
30
|
+
Decide from repository evidence without asking; this outranks plugin or agent workflow guidance.
|
|
31
|
+
|
|
32
|
+
- **Routine known-target Git/GitHub operation** (status, fetch, checkout, branch, merge, push, PR management): plan/goal-free unless it requires code edits, conflict resolution, destructive recovery, release/deploy, or design.
|
|
33
|
+
- **Consultation, explanation, or plan-only**: no goal.
|
|
34
|
+
- **Quick Path**: all must hold: requirements are unambiguous; the expected change touches about 3 files or fewer inside existing layers; no Heavy Path signal; no external-state mutation; verification is one or two commands. No `docs/plan` artifact and no repository goal: implement, run one focused check plus the project's normal check, and report. If any criterion fails during work, escalate to Light Path (create plan + goal) before further edits.
|
|
35
|
+
- **Light Path** (default): stays inside existing layers, adds no domain model, service class, or abstraction, and has no Heavy signal.
|
|
36
|
+
- **Heavy Path**: any clear signal: new directory, module, or architecture layer; new domain model, service class, or abstraction; security, authentication, authorization, or sessions; external API, message queue, or payment; DB schema or migration; concurrency, transaction boundaries, or cache invalidation; multi-domain refactor; the user asks for extra care ("신중하게", "검토 받고", "설계부터", "꼼꼼히").
|
|
37
|
+
|
|
38
|
+
Record three triage checkpoints: request (provisional), planning (after exploration), and code (in `pota-work`, just before the first edit; pending blocks mutation). Before the first edit the route may change either way; after editing starts, only Light → Heavy promotion is allowed. Every route change synchronizes the plan artifact, repository goal task roadmap through `goal replan`, native UI plan, and reasoning effort profile before mutation.
|
|
39
|
+
|
|
40
|
+
## Procedure (Light and Heavy Path)
|
|
41
|
+
|
|
42
|
+
1. Run `node "<pluginRoot>/scripts/pota.mjs" init --root "<repoRoot>"`. If unavailable, create `MEMORY.md`, `PLAN.md`, `docs/memory`, `docs/plan` and report that `pota doctor`/`pota upgrade` is needed.
|
|
43
|
+
2. Read `MEMORY.md` and `PLAN.md` when present; pick related notes and past plans (`planning-rules.md`). A non-trivial implementation request authorizes the plan and goal even if the user never says "goal".
|
|
44
|
+
3. Write the initial plan `docs/plan/<topic>.md` from `pota://templates/work-plan.md` plus its `PLAN.md` entry from the request and `MEMORY.md`: outcome, anticipated surfaces, stable roadmap, and `triage-plan` as first active task (investigation scope, toolcheck, Heavy criteria, update condition); mark it `initial: decision-complete for triage; not implementation-ready`.
|
|
45
|
+
4. Before exploration, toolcheck, delegation, or a non-blocking question, call Codex `update_plan` first and then `create_goal` when available (main agent only, subagents skip; record unavailability; never claim sync that did not occur), then start the repository goal:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
node "<pluginRoot>/scripts/pota.mjs" goal start --root "<repoRoot>" --goal "<objective>" --plan "docs/plan/<topic>.md" --tasks '[{"id":"triage-plan","title":"Run planned triage and finalize the plan","requiredEvidence":["planArtifact","triageDecision"]},{"id":"implement","title":"Implement the finalized plan"},{"id":"verify","title":"Verify and review the result"}]' --criteria "<completion criterion>" --session "<current session_id>"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Pass `--session` only with an ID the surface exposes; never invent one. Fold new work into an unfinished goal with `goal replan` (resume a paused or blocked goal first), never cancel it; start a separate goal only when the user explicitly switches work. When a hook reports another session owns the goal you continue, run `goal claim --session <id>` with the ID shown.
|
|
52
|
+
5. Run `triage-plan`: read the picked notes and plans and installed model guidance, run `toolcheck --root "<repoRoot>"`, and explore with focused search and LSP/symbol tools. Install tools only with explicit user approval.
|
|
53
|
+
6. Split the whole work into tasks each verifiable with two checks, each with `dependsOn` and `writeScope` in the `--tasks` JSON and named handoff files for cross-task data (`delegation.md`); design the test contract (requirements, invariants, boundaries, failure modes, expected failure) and dry-run/check modes.
|
|
54
|
+
7. Update the plan with route, checked Heavy signals, tool readiness, delegation, verification commands, stable IDs, and fail-closed required evidence (`planning-rules.md`). Mark it `decision-complete for implementation` and call `update_plan` again.
|
|
55
|
+
8. Only then record bound `planArtifact` and `triageDecision` evidence, complete `triage-plan`, and continue with `pota-work`.
|
|
56
|
+
9. Treat the user-called/main agent as the orchestrator: it plans, assigns, verifies, and records evidence. Implementation edits go to `worker` whenever subagent tools are available, on Light and Heavy Path; `explorer` does read-only discovery; without subagent tools the main agent takes over and records it.
|
|
57
|
+
10. Advance a UI step only after the matching repository task transition succeeds.
|
|
58
|
+
11. Ask the user only when evidence and local checks cannot decide. Use the OS locale for user-visible output and artifacts unless asked otherwise.
|
|
59
|
+
|
|
60
|
+
Model and reasoning effort: This skill explicitly instructs the effort level for delegated work. Pick each task's model tier (fast `haiku`, balanced `sonnet`, stronger `opus`, strongest `fable`) and request Light `medium`, Heavy `high`, final verification/review `xhigh`. Every new task/worker dispatch MUST pass the task's model and effort via the host option (Claude Code: Agent `model`/`effort`; never `fork`). Recording without passing is a defect. Record Requested/Effective model and effort per task; mark Heavy tasks `route: "heavy"` (details: `delegation.md`).
|
|
61
|
+
|
|
62
|
+
## Completion Criteria
|
|
63
|
+
|
|
64
|
+
- `PLAN.md` references the plan topic.
|
|
65
|
+
- `docs/plan/<topic>.md` includes goal, memory inputs, model guidance, tool readiness, complexity triage, selected path, agent assignments, module tasks, and two checks per task.
|
|
66
|
+
- The next implementation task can run without making planning decisions.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Pota Plan: Delegation and Reasoning Effort
|
|
2
|
+
|
|
3
|
+
Reference for `pota-plan`. Read before dispatching `explorer` or `worker` agents or recording reasoning effort. Quick Path work done directly by the main agent does not need this file.
|
|
4
|
+
|
|
5
|
+
## Subagent Lifecycle
|
|
6
|
+
|
|
7
|
+
- Treat the user-called/main agent as the orchestrator: it plans, assigns, verifies, and records evidence.
|
|
8
|
+
- Use `explorer` for independent read-only codebase discovery, impact analysis, pattern searches, and read-only verification that can run in parallel when the current tool list supports it.
|
|
9
|
+
- Use `worker` for actual implementation edits with clear file ownership whenever subagent tools are available, on both Light Path and Heavy Path.
|
|
10
|
+
- When the subagent tool provides a progress-reporting channel, require subagents to report status to the orchestrator when starting work, when deciding or confirming direction, during periodic progress, and when finishing; the orchestrator must immediately relay received status events to the user. If no mid-run reporting channel exists, relay observable events such as assignment, waiting, and final completion.
|
|
11
|
+
- Require delegated subagents to provide a final report before leaving; after capturing the final report and evidence, close or release the completed subagent, then review the final report and evidence before marking the work complete.
|
|
12
|
+
- When subagent tools are available, each agent may recursively delegate bounded sub-tasks to subagents, subject to the current surface's depth and concurrency limits.
|
|
13
|
+
- If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence; do not stop solely because subagent tools are unavailable.
|
|
14
|
+
- Keep urgent non-implementation blocking work local when the next step depends immediately on the result.
|
|
15
|
+
- Tell every delegated agent and child subagent to read project-root `MEMORY.md` when present and continue when absent, keep scope, and tell workers they are not alone in the codebase and must not revert others' edits.
|
|
16
|
+
- Put the selected `docs/memory/<domain>.md` note paths (picked from `MEMORY.md` entries) in the prompt `CONTEXT:` line. A delegated agent may not receive the session-start memory list, so do not rely on it reaching subagents.
|
|
17
|
+
- Host task-list tools (`update_plan`, `create_goal`, `wrap_up`) are called by the main agent only; a worker or subagent skips them (hosts reject them from subagents) and reports in its final report.
|
|
18
|
+
- Every plan must require these status, final-report, close-and-review, and recursive-delegation rules.
|
|
19
|
+
|
|
20
|
+
## Task Graph
|
|
21
|
+
|
|
22
|
+
- Every task in `goal start --tasks` / `goal replan --tasks` JSON declares `dependsOn` (task ids) and `writeScope` (literal repo-relative paths; no globs, `..`, or absolute paths). Example: `{"id":"api","title":"API","dependsOn":["triage-plan"],"writeScope":["src/api"]}`.
|
|
23
|
+
- Omitted `dependsOn` means the previous task in list order (first task: none); explicit `[]` means no dependency, so sequential plans keep working. In `goal replan` the default chains across completed and new tasks, and `dependsOn` may reference completed tasks. Duplicate ids, unknown ids, self references, and cycles are rejected.
|
|
24
|
+
- Omitted `writeScope` is null: that task can only be active alone. Give independent tasks disjoint scopes so they can run in parallel; scope is declarative, not filesystem-enforced.
|
|
25
|
+
- Pota has no messaging channel: name the handoff files that carry data between tasks in the plan (`Handoff files:`) and include them in the producer's `writeScope`.
|
|
26
|
+
|
|
27
|
+
## Delegation Prompt Format
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
TASK: Produce <role> input needed for the <topic> plan.
|
|
31
|
+
EXPECTED OUTCOME: Return concrete decisions, file paths, dependencies, risks, and evidence requirements.
|
|
32
|
+
REQUIRED TOOLS: Read-only repository exploration, tool readiness checks, LSP/symbol tools, local command checks.
|
|
33
|
+
MODEL: <tier/value> (passed via host option)
|
|
34
|
+
REASONING EFFORT: <value> (passed via host option)
|
|
35
|
+
MUST DO: Read the project-root `MEMORY.md` before doing assigned work. Preserve proper nouns exactly. Prefer repository evidence. Report missing tools and obtain explicit user approval before any installation. Identify assumptions. Follow the subagent lifecycle rules (status reporting when a channel exists, final report before leaving, recursive delegation only for bounded sub-tasks, passing each child's reasoning effort through the host option; report the exact model ID from your own system prompt in the final report, `unreported` if absent, never guess; skip host task-list tools `update_plan`, `create_goal`, `wrap_up`). Tell workers they are not alone in the codebase and must not revert others' edits. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts.
|
|
36
|
+
MUST NOT DO: Edit production code. Ask the user. Expand scope. Delegate vague or unbounded work.
|
|
37
|
+
CONTEXT: User request, MEMORY.md, selected `docs/memory/<domain>.md` note paths, applied model guidance, repository root, Pota CLI prefix line from the session-start hook.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Model and Reasoning Effort Routing
|
|
41
|
+
|
|
42
|
+
This skill explicitly instructs the effort level for delegated work. It also instructs each task's model.
|
|
43
|
+
|
|
44
|
+
- The plan decides each task's model from its nature and records `Requested model` and `Effective model`; models are not always inherited. Tiers: fast = read-only exploration, search, inventory, summarization (Claude Code `haiku`); balanced = Light Path implementation and routine verification (`sonnet`); stronger = planning, complexity triage, Heavy Path implementation, review, and final verification/review (`opus`); strongest = Heavy Path design only (`fable`).
|
|
45
|
+
- Model Effective is the passed value plus the exact model ID the worker reports from its own system prompt, e.g. `fable (claude-fable-5-1)`; when the report has no ID, record `<value> (unreported)` and never guess, via `goal assign --effective-model <id|unreported>`. Not passed is `not-applied: <reason>`; `inherited` only when the host has no model option. Codex passes the tier's slug (table below) through the host-native model option, else `inherited`.
|
|
46
|
+
- If a lower-tier worker fails a gate, re-dispatch that task one tier up (fast, balanced, stronger, strongest) before recording a blocker.
|
|
47
|
+
- A subagent's model and effort are fixed at dispatch. Any re-dispatch that changes either (one tier up, Light → Heavy promotion, route-change recalculation) MUST be a new dispatch (Claude Code: a new Agent call); never continue the existing subagent with `SendMessage`, which keeps its original model and effort.
|
|
48
|
+
- Routing is per dispatch only; never change model or global/user config files. `Model routing: inherited` in `MEMORY.md` or the plan disables per-task selection.
|
|
49
|
+
- Light Path planning, implementation, and verification request `medium`.
|
|
50
|
+
- Heavy Path planning, review, and implementation request `high`; final verification and review request `xhigh`.
|
|
51
|
+
- Every new task/worker dispatch MUST pass the task's requested effort through the host option. Recording without passing is a defect. Pass the chosen model the same way (Claude Code: Agent `model`). Child (recursive) dispatches follow the same rule.
|
|
52
|
+
- Every module task records `Requested reasoning effort` and `Effective reasoning effort` in `docs/plan/<topic>.md`. Requested is Pota's desired profile; Effective is decided by the dispatch call:
|
|
53
|
+
- Effort passed and not rejected: Effective is the requested value.
|
|
54
|
+
- Effort not passed: `not-applied: <reason>`; the reason is required.
|
|
55
|
+
- `inherited` only when the host exposes no effort option. Describe host capability only from the exposed interface or dispatch response, and distinguish Pota non-integration from host non-support.
|
|
56
|
+
|
|
57
|
+
| Host | Model / effort option | Effective |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| Claude Code | Agent tool `model` parameter (`haiku\|sonnet\|opus\|fable`). `effort` parameter (`low\|medium\|high\|xhigh\|max`), matching Pota values one to one. `subagent_type: "fork"` ignores `model` and `effort`, so never dispatch routed work as a fork. | requested value |
|
|
60
|
+
| Codex | Host-native model option (fast `gpt-6-luna`, balanced `gpt-6.1-terra`, stronger `gpt-6-sol`, strongest `gpt-6-astra`) and task/subagent reasoning-effort option or equivalent. | requested value |
|
|
61
|
+
| Antigravity | `invoke_subagent` `Model` takes only a tier enum: fast `flash_lite`, balanced `flash`, stronger `pro`, strongest `pro` (no tier above `pro`); `inherit` is not used for routed work. No effort option. | model: passed value; effort: `inherited` |
|
|
62
|
+
| Host without an option | None. | `inherited` |
|
|
63
|
+
|
|
64
|
+
- After each dispatch, record state with `goal assign --task <id> --agent <id> --effort <value> --effort-source <host-option|inherited|not-applied>`; for a `kind: "design"` task also `--model <value> --model-source host-option --host <claude-code|codex|antigravity>`. A design model that is not the host's strongest tier (`--host claude-code` `fable`, `codex` `gpt-6-astra`, `antigravity` `pro`; any of the three when `--host` is omitted) fails completion unless `--allow-no-design-model <reason>`. Mark Heavy tasks with `route: "heavy"` (tasks JSON field or `goal assign --route heavy`). A Heavy task with no effort, `not-applied`, or `inherited` fails `goal task --status completed` unless `--allow-inherited-effort <reason>` records why.
|
|
65
|
+
- On every route change, synchronize the plan artifact, repository goal task roadmap through `goal replan`, native UI plan, and reasoning effort profile before mutation. Completed task profiles are preserved as audit history; incomplete task profiles are recalculated for the new path.
|