alp-code 0.9.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.
Files changed (204) hide show
  1. package/CHANGELOG.md +770 -0
  2. package/LICENSE +21 -0
  3. package/README.md +295 -0
  4. package/alp.config.yaml +5 -0
  5. package/dist/src/agents/agent-definition.js +28 -0
  6. package/dist/src/agents/capability-catalog.js +33 -0
  7. package/dist/src/agents/compaction.js +36 -0
  8. package/dist/src/agents/errors.js +12 -0
  9. package/dist/src/agents/librarian.js +38 -0
  10. package/dist/src/agents/main.js +37 -0
  11. package/dist/src/agents/memory-grant.js +29 -0
  12. package/dist/src/agents/model-context.js +70 -0
  13. package/dist/src/agents/modes.js +134 -0
  14. package/dist/src/agents/oracle.js +36 -0
  15. package/dist/src/agents/read-thread.js +38 -0
  16. package/dist/src/agents/registry.js +238 -0
  17. package/dist/src/agents/render-identity.js +38 -0
  18. package/dist/src/agents/review.js +37 -0
  19. package/dist/src/agents/search.js +37 -0
  20. package/dist/src/agents/shared/house-rules.js +33 -0
  21. package/dist/src/agents/shared/principal.js +18 -0
  22. package/dist/src/agents/shared/voice.js +29 -0
  23. package/dist/src/agents/titling.js +32 -0
  24. package/dist/src/agents/types.js +15 -0
  25. package/dist/src/backend/execution-backend.js +2 -0
  26. package/dist/src/backend/local-execution-store.js +144 -0
  27. package/dist/src/backend/local-process-backend.js +533 -0
  28. package/dist/src/backend/local-supervisor.js +104 -0
  29. package/dist/src/cli/alp.js +380 -0
  30. package/dist/src/cli/commands/context.js +203 -0
  31. package/dist/src/cli/commands/delegate.js +136 -0
  32. package/dist/src/cli/commands/identity-sync.js +31 -0
  33. package/dist/src/cli/commands/init.js +184 -0
  34. package/dist/src/cli/commands/mode.js +22 -0
  35. package/dist/src/cli/commands/principal.js +114 -0
  36. package/dist/src/cli/commands/run-main.js +90 -0
  37. package/dist/src/cli/commands/runtime.js +21 -0
  38. package/dist/src/cli/mode-preference-store.js +62 -0
  39. package/dist/src/cli/mode-selector.js +178 -0
  40. package/dist/src/cli/update-check.js +77 -0
  41. package/dist/src/context/checkpoint.js +134 -0
  42. package/dist/src/context/compact-journal.js +153 -0
  43. package/dist/src/context/compact-payload.js +121 -0
  44. package/dist/src/context/continuity.js +70 -0
  45. package/dist/src/context/types.js +2 -0
  46. package/dist/src/delegation/backend-registry.js +40 -0
  47. package/dist/src/delegation/delegation-service.js +300 -0
  48. package/dist/src/delegation/types.js +12 -0
  49. package/dist/src/execution/execution-policy.js +96 -0
  50. package/dist/src/execution/execution-service.js +115 -0
  51. package/dist/src/execution/execution-store.js +78 -0
  52. package/dist/src/execution/identity-capsule.js +65 -0
  53. package/dist/src/execution/types.js +12 -0
  54. package/dist/src/hooks/execution-bridge.js +84 -0
  55. package/dist/src/index.js +4 -0
  56. package/dist/src/memory/adapters/markdown-file-store.js +257 -0
  57. package/dist/src/memory/adapters/memory-api-client.js +2 -0
  58. package/dist/src/memory/adapters/memory-path-mapper.js +76 -0
  59. package/dist/src/memory/adapters/remote-api-store.js +25 -0
  60. package/dist/src/memory/context-ranker.js +21 -0
  61. package/dist/src/memory/errors.js +58 -0
  62. package/dist/src/memory/memory-service.js +149 -0
  63. package/dist/src/memory/memory-store.js +2 -0
  64. package/dist/src/memory/types.js +2 -0
  65. package/dist/src/policy/capability-policy.js +29 -0
  66. package/dist/src/policy/delegation-policy.js +25 -0
  67. package/dist/src/policy/errors.js +10 -0
  68. package/dist/src/policy/invariants.js +31 -0
  69. package/dist/src/policy/memory-policy.js +22 -0
  70. package/dist/src/policy/policy-engine.js +85 -0
  71. package/dist/src/policy/types.js +8 -0
  72. package/dist/src/policy/workspace-policy.js +77 -0
  73. package/dist/src/principal/principal-profile-store.js +89 -0
  74. package/dist/src/runtime/adapter-files.js +147 -0
  75. package/dist/src/runtime/claude-adapter.js +177 -0
  76. package/dist/src/runtime/codex-adapter.js +169 -0
  77. package/dist/src/runtime/permission-rules.js +156 -0
  78. package/dist/src/runtime/render-session-context.js +124 -0
  79. package/dist/src/runtime/render-task-input.js +33 -0
  80. package/dist/src/runtime/runtime-adapter.js +2 -0
  81. package/dist/src/runtime/runtime-preference-store.js +66 -0
  82. package/dist/src/runtime/runtime-selector.js +178 -0
  83. package/dist/src/runtime/types.js +2 -0
  84. package/dist/src/runtime/windows-shim.js +57 -0
  85. package/dist/src/state-paths.js +49 -0
  86. package/dist/src/workflow/output-validator.js +27 -0
  87. package/dist/src/workflow/repair-policy.js +8 -0
  88. package/dist/src/workflow/types.js +22 -0
  89. package/dist/src/workflow/workflow-runner.js +81 -0
  90. package/hooks/compact-record.cjs +109 -0
  91. package/hooks/session-boot.cjs +112 -0
  92. package/hooks/session-end.cjs +34 -0
  93. package/package.json +48 -0
  94. package/scaffold/memory/INDEX.md +27 -0
  95. package/scaffold/memory/README.md +76 -0
  96. package/scaffold/memory/projects/INDEX.md +22 -0
  97. package/scaffold/memory/projects/PROTOCOL.md +128 -0
  98. package/scaffold/memory/projects/_template/PROJECT.md +45 -0
  99. package/scripts/alp.cjs +126 -0
  100. package/scripts/alp.ps1 +4 -0
  101. package/scripts/alp.sh +3 -0
  102. package/scripts/bootstrap.cjs +144 -0
  103. package/scripts/checkout-release.cjs +30 -0
  104. package/scripts/delegate.cjs +19 -0
  105. package/scripts/doctor.cjs +158 -0
  106. package/scripts/doctor.sh +3 -0
  107. package/scripts/ensure-state.cjs +22 -0
  108. package/scripts/lib/cli-link.cjs +375 -0
  109. package/scripts/lib/codex-role.cjs +18 -0
  110. package/scripts/lib/delegation/command-runner.cjs +108 -0
  111. package/scripts/lib/delegation/config.cjs +81 -0
  112. package/scripts/lib/install-paths.cjs +154 -0
  113. package/scripts/lib/release-manifest.cjs +42 -0
  114. package/scripts/lib/semver-lite.cjs +20 -0
  115. package/scripts/lib/state.cjs +274 -0
  116. package/scripts/lib/uninstall.cjs +252 -0
  117. package/scripts/lib/update-check-worker.cjs +21 -0
  118. package/scripts/lib/update.cjs +395 -0
  119. package/scripts/run-role.cjs +42 -0
  120. package/scripts/run-role.ps1 +4 -0
  121. package/scripts/run-role.sh +3 -0
  122. package/scripts/sync-project-index.sh +167 -0
  123. package/skills/agent-memory/SKILL.md +109 -0
  124. package/skills/alp-debug/SKILL.md +90 -0
  125. package/skills/alp-debug/references/defense-in-depth.md +118 -0
  126. package/skills/alp-debug/references/investigation-methodology.md +106 -0
  127. package/skills/alp-debug/references/log-and-ci-analysis.md +96 -0
  128. package/skills/alp-debug/references/performance-diagnostics.md +112 -0
  129. package/skills/alp-debug/references/reporting-standards.md +120 -0
  130. package/skills/alp-debug/references/root-cause-tracing.md +134 -0
  131. package/skills/alp-debug/references/systematic-debugging.md +93 -0
  132. package/skills/alp-debug/references/verification.md +86 -0
  133. package/skills/alp-debug/scripts/find-polluter.sh +63 -0
  134. package/skills/alp-debug/scripts/find-polluter.test.md +102 -0
  135. package/skills/alp-plan/SKILL.md +128 -0
  136. package/skills/alp-plan/references/archive-workflow.md +77 -0
  137. package/skills/alp-plan/references/codebase-understanding.md +55 -0
  138. package/skills/alp-plan/references/output-standards.md +96 -0
  139. package/skills/alp-plan/references/plan-organization.md +129 -0
  140. package/skills/alp-plan/references/red-team-personas.md +76 -0
  141. package/skills/alp-plan/references/red-team-workflow.md +81 -0
  142. package/skills/alp-plan/references/research-phase.md +57 -0
  143. package/skills/alp-plan/references/scope-challenge.md +82 -0
  144. package/skills/alp-plan/references/solution-design.md +76 -0
  145. package/skills/alp-plan/references/validate-question-framework.md +89 -0
  146. package/skills/alp-plan/references/validate-workflow.md +83 -0
  147. package/skills/alp-predict/SKILL.md +98 -0
  148. package/skills/alp-scenario/SKILL.md +86 -0
  149. package/skills/code-review/SKILL.md +111 -0
  150. package/skills/code-review/references/code-review-reception.md +114 -0
  151. package/skills/code-review/references/edge-case-scouting.md +78 -0
  152. package/skills/code-review/references/verification-before-completion.md +117 -0
  153. package/skills/delegation/SKILL.md +46 -0
  154. package/skills/docs-seeker/.env.example +15 -0
  155. package/skills/docs-seeker/SKILL.md +87 -0
  156. package/skills/docs-seeker/package.json +25 -0
  157. package/skills/docs-seeker/references/advanced.md +82 -0
  158. package/skills/docs-seeker/references/context7-patterns.md +68 -0
  159. package/skills/docs-seeker/references/errors.md +72 -0
  160. package/skills/docs-seeker/scripts/analyze-llms-txt.js +211 -0
  161. package/skills/docs-seeker/scripts/detect-topic.js +172 -0
  162. package/skills/docs-seeker/scripts/fetch-docs.js +213 -0
  163. package/skills/docs-seeker/scripts/tests/run-tests.js +72 -0
  164. package/skills/docs-seeker/scripts/tests/test-analyze-llms.js +119 -0
  165. package/skills/docs-seeker/scripts/tests/test-detect-topic.js +112 -0
  166. package/skills/docs-seeker/scripts/tests/test-fetch-docs.js +84 -0
  167. package/skills/docs-seeker/scripts/utils/env-loader.js +94 -0
  168. package/skills/docs-seeker/workflows/library-search.md +73 -0
  169. package/skills/docs-seeker/workflows/repo-analysis.md +90 -0
  170. package/skills/docs-seeker/workflows/topic-search.md +69 -0
  171. package/skills/git/SKILL.md +121 -0
  172. package/skills/git/references/branch-management.md +90 -0
  173. package/skills/git/references/commit-standards.md +82 -0
  174. package/skills/git/references/gh-cli-guide.md +132 -0
  175. package/skills/git/references/safety-protocols.md +86 -0
  176. package/skills/git/references/workflow-commit.md +89 -0
  177. package/skills/git/references/workflow-merge.md +63 -0
  178. package/skills/git/references/workflow-pr.md +70 -0
  179. package/skills/git/references/workflow-push.md +62 -0
  180. package/skills/gkg/SKILL.md +87 -0
  181. package/skills/gkg/references/cli-commands.md +92 -0
  182. package/skills/gkg/references/http-api.md +99 -0
  183. package/skills/gkg/references/language-support.md +54 -0
  184. package/skills/problem-solving/SKILL.md +86 -0
  185. package/skills/problem-solving/references/attribution.md +48 -0
  186. package/skills/problem-solving/references/collision-zone-thinking.md +71 -0
  187. package/skills/problem-solving/references/inversion-exercise.md +88 -0
  188. package/skills/problem-solving/references/meta-pattern-recognition.md +80 -0
  189. package/skills/problem-solving/references/scale-game.md +82 -0
  190. package/skills/problem-solving/references/simplification-cascades.md +83 -0
  191. package/skills/problem-solving/references/when-stuck.md +76 -0
  192. package/skills/repomix/SKILL.md +94 -0
  193. package/skills/repomix/references/configuration.md +134 -0
  194. package/skills/repomix/references/usage-patterns.md +106 -0
  195. package/skills/repomix/scripts/.coverage +0 -0
  196. package/skills/repomix/scripts/README.md +179 -0
  197. package/skills/repomix/scripts/repomix_batch.py +455 -0
  198. package/skills/repomix/scripts/repos.example.json +15 -0
  199. package/skills/repomix/scripts/requirements.txt +15 -0
  200. package/skills/repomix/scripts/tests/test_repomix_batch.py +531 -0
  201. package/skills/research/SKILL.md +107 -0
  202. package/skills/security-scan/SKILL.md +101 -0
  203. package/skills/security-scan/references/secret-patterns.md +75 -0
  204. package/skills/security-scan/references/vulnerability-patterns.md +136 -0
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // Pre/PostCompact hook: appends exactly one line to context/compact-events.jsonl.
5
+ //
6
+ // Zero dependency, on purpose (plan §1): this hook writes a raw-but-filtered envelope, never
7
+ // normalizes it, never reads the journal back, and never touches checkpoint.json or
8
+ // continuity.md. Normalizing into a CompactEventV1 happens later, in TypeScript with Zod, when
9
+ // `alp context status|validate` replays the journal — so this file cannot regress a compiled
10
+ // dependency graph and stays a single `appendFileSync` at its core.
11
+ //
12
+ // Fail-open by design (invariant 7): every branch below either appends one line or does
13
+ // nothing, and nothing here may throw past `main()`. A missing execution ID means this launch
14
+ // did not go through ALP at all — that is not an error, just silence.
15
+ //
16
+ // argv: `compact-record.cjs <pre|post> <claude|codex>`. Both are supplied by the adapter that
17
+ // registers the hook, not read from the payload — trusting the payload's own idea of its phase
18
+ // or runtime would let it pick which bucket it lands in.
19
+
20
+ const fs = require("node:fs");
21
+
22
+ const MAX_STDIN_BYTES = 1024 * 1024;
23
+ const MAX_LINE_BYTES = 16 * 1024;
24
+ const MAX_VALUE_LENGTH = 256;
25
+ const EXECUTION_ID_PATTERN = /^exec_[a-zA-Z0-9_-]+$/;
26
+
27
+ // Mirrors `SOURCE_WHITELIST` in src/context/compact-payload.ts. Kept as a second copy rather
28
+ // than a shared import: this file has no dependency graph to begin with, and the two lists are
29
+ // pinned against the same measured schema (§Runtime capability), so a drift between them would
30
+ // show up immediately as a normalizer that drops a field the hook thought it kept.
31
+ const SOURCE_WHITELIST = {
32
+ claude: ["session_id", "trigger", "model", "prompt_id", "agent_id", "agent_type"],
33
+ codex: ["session_id", "trigger", "model", "turn_id", "agent_id", "agent_type"],
34
+ };
35
+
36
+ function filterSource(runtime, payload) {
37
+ const whitelist = SOURCE_WHITELIST[runtime] || [];
38
+ const source = {};
39
+ if (payload !== null && typeof payload === "object") {
40
+ for (const key of whitelist) {
41
+ const value = payload[key];
42
+ if (typeof value === "string") source[key] = value.slice(0, MAX_VALUE_LENGTH);
43
+ else if (typeof value === "number" || typeof value === "boolean") source[key] = String(value);
44
+ }
45
+ }
46
+ return source;
47
+ }
48
+
49
+ /** Reads stdin fully; returns `null` rather than the buffer when it exceeds the 1 MiB cap. */
50
+ function readStdin() {
51
+ let buffer;
52
+ try {
53
+ buffer = fs.readFileSync(0);
54
+ } catch {
55
+ return Buffer.alloc(0);
56
+ }
57
+ return buffer.length > MAX_STDIN_BYTES ? null : buffer;
58
+ }
59
+
60
+ function main() {
61
+ const phase = process.argv[2];
62
+ const runtime = process.argv[3];
63
+ if (phase !== "pre" && phase !== "post") return;
64
+ if (runtime !== "claude" && runtime !== "codex") return;
65
+
66
+ // A native launch that never went through `alp` sets none of this — silence, not an error.
67
+ const executionId = process.env.ALP_DELEGATION_EXECUTION_ID || "";
68
+ const policyHash = process.env.ALP_POLICY_HASH || "";
69
+ const journal = process.env.ALP_COMPACT_EVENTS || "";
70
+ if (!EXECUTION_ID_PATTERN.test(executionId) || policyHash.length === 0 || journal.length === 0) return;
71
+
72
+ const buffer = readStdin();
73
+ let source;
74
+ if (buffer === null) {
75
+ source = { parseError: "stdin exceeded 1 MiB" };
76
+ } else {
77
+ try {
78
+ source = filterSource(runtime, JSON.parse(buffer.length === 0 ? "{}" : buffer.toString("utf8")));
79
+ } catch (error) {
80
+ // Name the failure only — never the content that failed to parse.
81
+ source = { parseError: String(error && error.message || "invalid JSON").slice(0, MAX_VALUE_LENGTH) };
82
+ }
83
+ }
84
+
85
+ const envelope = {
86
+ v: 1,
87
+ at: new Date().toISOString(),
88
+ executionId,
89
+ policyHash,
90
+ runtime,
91
+ phase,
92
+ source,
93
+ };
94
+ const line = `${JSON.stringify(envelope)}\n`;
95
+ if (Buffer.byteLength(line, "utf8") > MAX_LINE_BYTES) return;
96
+
97
+ try {
98
+ fs.appendFileSync(journal, line, { encoding: "utf8", mode: 0o600, flag: "a" });
99
+ } catch {
100
+ // A read-only or missing journal must never fail the compaction it is recording.
101
+ }
102
+ }
103
+
104
+ try {
105
+ main();
106
+ } catch {
107
+ // Nothing above should throw, but this hook's contract is exit 0 no matter what.
108
+ }
109
+ process.exit(0);
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // SessionStart hook: puts the session context into the model's view before turn 1, and — for
5
+ // every `source` including `"compact"` — the continuity checkpoint right after it.
6
+ //
7
+ // This is the *only* channel ALP identity and continuity travel on, for both runtimes. Claude
8
+ // Code and Codex CLI both turn `additionalContext` into a developer-role message ahead of the
9
+ // first user turn, which is exactly the guarantee an interactive session needs: fully briefed,
10
+ // zero turns spent. Measured 2026-09-04: `SessionStart(source="compact")` is the one point both
11
+ // runtimes agree on for reinjection after native compaction — Claude fires it *during*
12
+ // compaction, Codex at the start of the next turn — so this hook runs unchanged for every
13
+ // `source` rather than branching on it.
14
+ //
15
+ // Two identity sources, in priority order:
16
+ // 1. `ALP_SESSION_CONTEXT` — this execution's own file, written by the runtime adapter.
17
+ // It carries identity plus the invariants, policy context and workspace grant that
18
+ // only ALP knows. Every session launched through `alp` gets this.
19
+ // 2. `.alp/agents/<role>.md` — the static role document, for the native path where the
20
+ // principal ran `claude`/`codex` directly and no adapter was involved.
21
+ //
22
+ // One continuity source, appended after whichever identity source loaded: `ALP_CONTINUITY_
23
+ // CONTEXT`, the pre-rendered `continuity.md` (plan §9). It is optional in a way identity is
24
+ // not — a fresh execution with no pins yet renders to an empty string, and `alp` launches
25
+ // with the bridge flag off never carry an objective worth mentioning either — so a missing or
26
+ // empty file is quiet, not a warning. Only a continuity file that exists but cannot be trusted
27
+ // (unreadable, or over the same 24 KiB bound the renderer itself enforces) gets one, because
28
+ // that state means something ALP wrote is no longer being read back correctly.
29
+ //
30
+ // Speed is why both sources are pre-rendered files: no `dist/` load, no registry
31
+ // construction, no TypeScript runtime, no Zod. Pointing the agent at a file and asking it to
32
+ // Read would cost a tool round-trip before any real work starts.
33
+ //
34
+ // Fail-open by design, unlike the policy hooks: if context cannot be loaded, the session
35
+ // still starts and the reason surfaces as a warning rather than being swallowed. Managed
36
+ // launches fail closed earlier and elsewhere — the adapter writes these files before it
37
+ // spawns anything, so an unwritable one aborts `prepare()` and no process ever starts.
38
+
39
+ const fs = require("node:fs");
40
+ const path = require("node:path");
41
+ const P = require("../scripts/lib/install-paths.cjs");
42
+
43
+ const ROLE_PATTERN = /^[a-z][a-z0-9-]*$/;
44
+ // Same bound as `renderContinuity`'s `MAX_RENDERED_BYTES` (plan §9) — there is only one
45
+ // injection limit, so a file over it did not come out of that renderer and is not trusted.
46
+ const MAX_CONTINUITY_BYTES = 24 * 1024;
47
+
48
+ function emit(context, warning) {
49
+ process.stdout.write(JSON.stringify({
50
+ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: context },
51
+ ...(warning ? { systemMessage: `⚠️ ${warning}` } : {}),
52
+ }));
53
+ }
54
+
55
+ function repoRoot() {
56
+ return process.env.ALP_REPO_ROOT || path.join(__dirname, "..");
57
+ }
58
+
59
+ function loadSessionContext() {
60
+ const sessionContext = process.env.ALP_SESSION_CONTEXT;
61
+ if (sessionContext) return fs.readFileSync(sessionContext, "utf8");
62
+ const role = process.env.ALP_ROLE || "main";
63
+ if (!ROLE_PATTERN.test(role)) throw new Error(`invalid role \`${role}\``);
64
+ // `~/.alp/agents`, không phải `<thư mục cài>/.alp/agents`: từ v0.9.0 thư mục cài là artifact
65
+ // bị thay nguyên khối mỗi lần update, nên tài liệu identity sinh trong đó biến mất đúng lúc
66
+ // hook này cần đọc. Vẫn thử chỗ cũ để một bản cài chưa `alp init` lại vẫn boot có identity.
67
+ const file = path.join(P.agentsDir(process.env), `${role}.md`);
68
+ try {
69
+ return fs.readFileSync(file, "utf8");
70
+ } catch (error) {
71
+ if (error.code !== "ENOENT") throw error;
72
+ const legacy = path.join(repoRoot(), ".alp", "agents", `${role}.md`);
73
+ try {
74
+ return fs.readFileSync(legacy, "utf8");
75
+ } catch {
76
+ throw error;
77
+ }
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Best-effort load of `continuity.md`. Never throws: a problem here must not cost the
83
+ * session its identity, which is why it is read after — and independently of —
84
+ * `loadSessionContext()` rather than folded into the same try/catch.
85
+ */
86
+ function loadContinuity() {
87
+ const file = process.env.ALP_CONTINUITY_CONTEXT;
88
+ if (!file) return { text: "", warning: null };
89
+ let content;
90
+ try {
91
+ content = fs.readFileSync(file, "utf8");
92
+ } catch (error) {
93
+ // Missing is the common case — a bridge-off launch, or an execution from before this
94
+ // feature — and not worth a warning. Anything else (a directory, a permissions error)
95
+ // means the path is wrong in a way worth surfacing.
96
+ return { text: "", warning: error.code === "ENOENT" ? null : `ALP continuity not loaded: ${error.message}` };
97
+ }
98
+ if (content.trim().length === 0) return { text: "", warning: null };
99
+ if (Buffer.byteLength(content, "utf8") > MAX_CONTINUITY_BYTES) {
100
+ return { text: "", warning: "ALP continuity exceeds its injection bound and was skipped" };
101
+ }
102
+ return { text: content, warning: null };
103
+ }
104
+
105
+ try {
106
+ const sessionContext = loadSessionContext();
107
+ const continuity = loadContinuity();
108
+ const context = continuity.text.length > 0 ? `${sessionContext}\n\n${continuity.text}` : sessionContext;
109
+ emit(context, continuity.warning);
110
+ } catch (error) {
111
+ emit("", `ALP identity not loaded: ${error.message}. Run \`alp identity sync\`.`);
112
+ }
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // Stop hook: records the execution's final answer and closes out its state file.
5
+ //
6
+ // This hook does NOT judge the answer. It used to parse the last assistant message as
7
+ // JSON and reply `{"decision":"block"}` when that failed, which forced every role —
8
+ // including the principal-facing one — to speak JSON instead of prose. Roles now return
9
+ // text, so the only job left here is bookkeeping: write the output into `state.json` so
10
+ // `run-main.ts` and `delegation-service.ts` can reconcile status afterwards.
11
+ //
12
+ // Fail-open by design. A bookkeeping error must never trap a finished session.
13
+
14
+ const fs = require("node:fs");
15
+ const path = require("node:path");
16
+
17
+ function note(message) { process.stdout.write(JSON.stringify({ systemMessage: message })); }
18
+
19
+ async function main() {
20
+ const executionId = process.env.ALP_DELEGATION_EXECUTION_ID || "";
21
+ try {
22
+ const payload = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
23
+ const bridge = require(path.join(__dirname, "..", "dist", "src", "hooks", "execution-bridge.js"));
24
+ const output = payload.last_assistant_message ?? payload.output ?? payload.final_output ?? payload.result;
25
+ const result = await bridge.finalizeExecution({ executionId, output });
26
+ return note(result.ok
27
+ ? `execution ${executionId} finalized`
28
+ : `execution ${executionId} finalized with issues: ${result.issues.join("; ")}`);
29
+ } catch (error) {
30
+ return note(`execution ${executionId} could not be finalized: ${error.message}`);
31
+ }
32
+ }
33
+
34
+ main();
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "alp-code",
3
+ "version": "0.9.0",
4
+ "description": "Code-native agent layer for Claude Code and Codex CLI",
5
+ "license": "MIT",
6
+ "author": "Phúc Anh (https://github.com/phucanh08)",
7
+ "bin": {
8
+ "alp": "scripts/alp.cjs"
9
+ },
10
+ "files": [
11
+ "dist/src",
12
+ "scripts",
13
+ "!scripts/test-*.cjs",
14
+ "!scripts/cut-release.cjs",
15
+ "!scripts/pack-release.cjs",
16
+ "!scripts/probe-compact-hooks.cjs",
17
+ "hooks",
18
+ "skills",
19
+ "scaffold",
20
+ "alp.config.yaml",
21
+ "CHANGELOG.md"
22
+ ],
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/phucanh08/alp-code.git"
29
+ },
30
+ "homepage": "https://github.com/phucanh08/alp-code#readme",
31
+ "bugs": {
32
+ "url": "https://github.com/phucanh08/alp-code/issues"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc -p tsconfig.json",
36
+ "typecheck": "tsc -p tsconfig.json --noEmit",
37
+ "test": "vitest run",
38
+ "prepack": "npm run build"
39
+ },
40
+ "dependencies": {
41
+ "zod": "^4.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^24.0.0",
45
+ "typescript": "^5.9.0",
46
+ "vitest": "^3.2.0"
47
+ }
48
+ }
@@ -0,0 +1,27 @@
1
+ # INDEX — mục lục trí nhớ dùng chung
2
+
3
+ > **Mục lục, không phải nơi chứa nội dung.** Mỗi dòng trỏ tới một file trong `shared/`.
4
+ > Memory grant của vai (khai trong `src/agents/`) **lọc** danh sách này khi dựng execution
5
+ > — mỗi vai chỉ thấy dòng trỏ tới thứ nó được đọc. Quy ước & luật ghi: [`README.md`](README.md).
6
+ >
7
+ > **Phạm vi:** chỉ fact **xuyên project**. Thứ gắn với một project ở `projects/<slug>/`.
8
+ > Nháp riêng của một vai ở `private/<role>/` và **không** được liệt kê ở đây.
9
+
10
+ ## Quyết định chung
11
+
12
+ _Chưa có._
13
+
14
+ ## Con người & tổ chức
15
+
16
+ _Chưa có._
17
+
18
+ ## Tham chiếu
19
+
20
+ _Chưa có._
21
+
22
+ ## Nhật ký phiên
23
+
24
+ _Chỉ giữ các mốc gần nhất. Diễn biến đầy đủ ở `projects/<slug>/log/`._
25
+
26
+ | Ngày | Nội dung chính |
27
+ |---|---|
@@ -0,0 +1,76 @@
1
+ # memory/ — trí nhớ dùng chung của cả hệ
2
+
3
+ Mục lục: [`INDEX.md`](INDEX.md). Thư mục này chứa nội dung thật.
4
+ Luật ghi đầy đủ: skill `agent-memory`.
5
+
6
+ ## Ba khoang, ba mục đích khác nhau
7
+
8
+ ```
9
+ memory/
10
+ ├── shared/ fact xuyên project — MỌI vai đọc được
11
+ │ ├── decisions/ quyết định chung, không thuộc project nào
12
+ │ ├── people/ người & tổ chức
13
+ │ └── reference/ link, dashboard, ticket, tài liệu ngoài
14
+ ├── projects/ Project Layer 3 tầng — MỌI vai đọc được, quyền ghi tuỳ vai
15
+ │ ├── INDEX.md L0 — bảng tổng, sinh tự động
16
+ │ ├── PROTOCOL.md giao thức 3 tầng
17
+ │ └── <slug>/ L1 PROJECT.md · L2 decisions|log|refs
18
+ └── private/ nháp riêng từng vai — CHỈ vai đó đọc được
19
+ └── <role>/
20
+ ```
21
+
22
+ ## Ranh giới — luật quan trọng nhất của thư mục này
23
+
24
+ | Loại nội dung | Đích | Vì sao |
25
+ |---|---|---|
26
+ | Fact về principal | `identity/_shared/PRINCIPAL.md` | một bản duy nhất cho mọi vai |
27
+ | Fact xuyên project | `shared/` | mọi vai cần thấy |
28
+ | Fact về một project | `projects/<slug>/` | một fact một nhà |
29
+ | Nháp, giả thuyết chưa kiểm chứng | `private/<role>/` | chưa đáng tin, đừng lan ra |
30
+ | Bài học về chính agent | `identity/<role>/journal/` | không phải fact về thế giới |
31
+
32
+ **Cấm:** ghi fact về principal / project / thế giới vào `private/`.
33
+ Làm vậy = fact bị nhân bản giữa các vai rồi lệch nhau, và không vai nào biết bản nào đúng.
34
+ `private/` **chỉ** chứa thứ mà nếu mất đi cũng không ai thiệt.
35
+
36
+ ## Cách ly hai chiều
37
+
38
+ `private/<role>/` là riêng **thật**. Main **không** đọc được private của Search,
39
+ Librarian hay Read Thread và ngược lại. Không có vai nào là root. Muốn biết vai khác nghĩ
40
+ gì → hỏi vai đó.
41
+
42
+ ## Định dạng chuẩn mỗi file
43
+
44
+ ```markdown
45
+ ---
46
+ id: <slug-kebab-case>
47
+ type: decision | person | reference | log | project
48
+ layer: L1 | L2 | L3
49
+ visibility: private | team
50
+ owner: <role>
51
+ created: YYYY-MM-DD
52
+ updated: YYYY-MM-DD
53
+ tags: []
54
+ source: <link | phiên>
55
+ ---
56
+
57
+ # <Tiêu đề>
58
+
59
+ <Nội dung. Một fact chính mỗi file.>
60
+
61
+ **Vì sao quan trọng:** <một dòng>
62
+ **Áp dụng thế nào:** <một dòng>
63
+
64
+ Liên quan: [[slug-khac]]
65
+ ```
66
+
67
+ ## Luật
68
+
69
+ - **Một fact = một file.** Đừng dồn.
70
+ - **Ngày tuyệt đối.** "tuần sau" → `2026-08-21`.
71
+ - **Kiểm tra trùng trước khi tạo.** Có rồi thì cập nhật.
72
+ - **Sai thì xoá.** Trí nhớ sai nguy hiểm hơn không nhớ.
73
+ - **Không ghi thứ repo đã ghi** — cấu trúc code, lịch sử git, nội dung CLAUDE.md.
74
+ - Mỗi file mới trong `shared/` → thêm một dòng vào [`INDEX.md`](INDEX.md).
75
+ - Liên kết chéo bằng `[[slug]]`. Link tới file chưa tồn tại là bình thường — ghi chú
76
+ cho việc cần viết sau, không phải lỗi.
@@ -0,0 +1,22 @@
1
+ # INDEX — L0
2
+
3
+ > Tầng luôn được nạp. Một dòng mỗi project, không hơn.
4
+ > Chi tiết nằm ở `<slug>/PROJECT.md` (L1) — chỉ mở khi project đó nằm trong phạm vi phiên.
5
+ > Giao thức: [`PROTOCOL.md`](PROTOCOL.md). Sinh lại bằng `scripts/sync-project-index.sh --write`.
6
+
7
+ **Đừng sửa tay vùng giữa hai marker.** Sửa frontmatter ở L1 rồi chạy `--write`.
8
+
9
+ ## Đang chạy
10
+
11
+ <!-- BEGIN:INDEX -->
12
+ | Slug | P | Trạng thái | Tóm tắt | Cập nhật |
13
+ |---|---|---|---|---|
14
+ <!-- END:INDEX -->
15
+
16
+ ## Đã đóng
17
+
18
+ <!-- BEGIN:DONE -->
19
+ _(chưa có)_
20
+ <!-- END:DONE -->
21
+
22
+ Chú giải trạng thái & ưu tiên: [`PROTOCOL.md`](PROTOCOL.md).
@@ -0,0 +1,128 @@
1
+ # PROJECT LAYER — Giao thức
2
+
3
+ > Cách Phở biết về các project mà không đốt hết context.
4
+ > Mô hình: **progressive disclosure 3 tầng**, kiểm soát bằng `modified`.
5
+ > Tham chiếu: Hermes Agent skills system (L0 index → L1 content → L2 refs).
6
+
7
+ ## 1. Ba tầng
8
+
9
+ | Tầng | File | Khi nào nạp | Ngân sách |
10
+ |---|---|---|---|
11
+ | **L0 — INDEX** | `projects/INDEX.md` | **Luôn**, ở bước boot | ≤ 1 dòng/project, mục tiêu < 2k token kể cả khi có 30 project |
12
+ | **L1 — CARD** | `projects/<slug>/PROJECT.md` | Khi project đó nằm trong phạm vi phiên | ≤ 60 dòng |
13
+ | **L2 — REFS** | `projects/<slug>/{decisions,log,refs}/*.md` | Chỉ khi cần đúng file đó | tuỳ ý |
14
+
15
+ **Luật vàng:** không bao giờ nạp L1 của mọi project. Đọc L0, xác định project liên quan,
16
+ chỉ mở L1 của nó. L2 chỉ mở khi L1 trỏ tới và câu hỏi thật sự cần.
17
+
18
+ Nếu principal hỏi "tình hình chung thế nào?" → **chỉ cần L0**. Mở L1 lúc này là lãng phí.
19
+
20
+ ## 2. Vì sao L0 phải ổn định
21
+
22
+ L0 nằm trong phần đầu context mỗi phiên. Mỗi lần nó đổi byte, prompt cache hỏng và toàn bộ
23
+ phần sau phải tính lại. Nên:
24
+
25
+ - **L0 chỉ đổi khi trạng thái project đổi thật** — không sửa vì lý do vụn vặt.
26
+ - **Ngày trong L0 ghi theo ngày, không ghi giờ** (`2026-08-14`, không `2026-08-14 13:55`).
27
+ Timestamp mịn hơn ngày sẽ phá cache mỗi lần chạm file.
28
+ - **Thông tin hay đổi không đặt ở L0** — tiến độ chi tiết, việc đang làm, ghi chú phiên
29
+ thuộc về L1/L2.
30
+ - Sắp xếp hàng trong bảng **cố định** (theo priority rồi slug), không sắp lại tuỳ hứng.
31
+
32
+ ## 3. Kiểm soát bằng `modified`
33
+
34
+ Có hai đồng hồ, và chúng phải khớp nhau:
35
+
36
+ | Đồng hồ | Ai ghi | Ý nghĩa |
37
+ |---|---|---|
38
+ | `updated:` trong frontmatter L1 | **Phở**, thủ công | "Lần cuối Phở thật sự xem lại và xác nhận nội dung này đúng" |
39
+ | mtime của file (hệ thống) | Máy | "Lần cuối file bị chạm vào" |
40
+
41
+ ### Ba tín hiệu
42
+
43
+ **DRIFT** — `mtime > updated`
44
+ File đã bị sửa mà `updated` chưa được cập nhật. Nghĩa là có người (hoặc agent khác) sửa
45
+ ngoài quy trình, hoặc Phở sửa mà quên đóng dấu. → Đọc lại L1, xác nhận nội dung, cập nhật
46
+ `updated`, đồng bộ lại dòng L0 nếu cần.
47
+
48
+ **STALE** — `status: ACTIVE` và `updated` cách hôm nay > 14 ngày
49
+ Project khai là đang chạy nhưng không ai đụng tới hai tuần. Một trong hai điều sai: hoặc
50
+ trạng thái sai, hoặc project thật sự bị bỏ quên. → Nêu trong heartbeat, hỏi principal.
51
+
52
+ **ORPHAN** — có `PROJECT.md` mà không có dòng trong L0, hoặc ngược lại
53
+ → Đồng bộ ngay. L0 là nguồn sự thật về *danh sách*; L1 là nguồn sự thật về *nội dung*.
54
+
55
+ ### Công cụ
56
+
57
+ ```bash
58
+ scripts/sync-project-index.sh # quét, báo cáo DRIFT / STALE / ORPHAN
59
+ scripts/sync-project-index.sh --write # ghi lại bảng L0 từ frontmatter L1
60
+ ```
61
+
62
+ Chạy ở bước boot (rẻ, chỉ đọc frontmatter) và trong mỗi heartbeat.
63
+ `--write` chỉ đụng vùng giữa `<!-- BEGIN:INDEX -->` và `<!-- END:INDEX -->`.
64
+
65
+ ## 4. Cấu trúc một project
66
+
67
+ ```
68
+ projects/<slug>/
69
+ ├── PROJECT.md L1 — card, có frontmatter
70
+ ├── decisions/ L2 — mỗi quyết định một file, YYMMDD-slug.md
71
+ ├── log/ L2 — nhật ký phiên, YYYY-MM.md (gộp theo tháng)
72
+ └── refs/ L2 — link, spec, tài liệu ngoài
73
+ ```
74
+
75
+ Thư mục L2 tạo khi cần, không tạo sẵn cho rỗng.
76
+
77
+ ## 5. Frontmatter L1 — bắt buộc
78
+
79
+ ```yaml
80
+ ---
81
+ slug: ten-project # trùng tên thư mục
82
+ name: Tên đầy đủ
83
+ status: ACTIVE # ACTIVE | WAITING | BLOCKED | PAUSED | DONE
84
+ priority: P1 # P0 khẩn | P1 chính | P2 phụ | P3 nền
85
+ summary: Một câu ≤ 100 ký tự — dòng này lên thẳng L0
86
+ path: ~/AnhlpProjects/ten-project
87
+ updated: 2026-08-14 # ngày, không giờ
88
+ ---
89
+ ```
90
+
91
+ `summary` là thứ duy nhất từ L1 được phép leo lên L0. Viết nó cho người đọc lướt: nói
92
+ project *làm gì*, không nói nó *đang ở đâu* (trạng thái đã có cột riêng).
93
+
94
+ ## 6. Vòng đời
95
+
96
+ **Thêm project**
97
+ 1. `cp -r projects/_template projects/<slug>`
98
+ 2. Điền frontmatter + card
99
+ 3. `scripts/sync-project-index.sh --write`
100
+
101
+ **Đổi trạng thái**
102
+ Sửa `status` + `updated` ở L1 → `--write` → L0 tự cập nhật. Không sửa tay L0.
103
+
104
+ **Đóng project**
105
+ `status: DONE` → `--write` đẩy nó xuống mục Đã đóng ở L0 (một dòng, không chi tiết).
106
+ Giữ thư mục lại; lịch sử có giá.
107
+
108
+ ## 7. Ranh giới với phần còn lại của `memory/`
109
+
110
+ Dễ nhầm, nên nói rõ:
111
+
112
+ | Thuộc về | Đặt ở |
113
+ |---|---|
114
+ | Bất cứ thứ gì gắn với **một** project | `memory/projects/<slug>/` |
115
+ | Fact xuyên project (người, tài khoản, quyết định chung) | `memory/shared/` |
116
+ | Sở thích ổn định của principal | `identity/_shared/PRINCIPAL.md` |
117
+ | Nháp chưa kiểm chứng của một vai | `memory/private/<role>/` |
118
+
119
+ Một fact chỉ có một nhà. Luật đầy đủ: skill `agent-memory`.
120
+
121
+ **Quyền ghi khác nhau theo vai.** `PROJECT.md` (L1) và `decisions/` là quyền của
122
+ main. `refs/` mở cho vai research. Xem `identity/<role>/loadout.yaml`.
123
+
124
+ ## 8. Chú giải L0
125
+
126
+ **Trạng thái:** `ACTIVE` đang làm · `WAITING` chờ bên ngoài · `BLOCKED` cần principal gỡ ·
127
+ `PAUSED` cố ý dừng · `DONE` đã đóng
128
+ **Ưu tiên:** `P0` khẩn · `P1` chính · `P2` phụ · `P3` nền
@@ -0,0 +1,45 @@
1
+ ---
2
+ slug: ten-project
3
+ name: Tên đầy đủ của project
4
+ status: ACTIVE
5
+ priority: P1
6
+ summary: Một câu ≤ 100 ký tự, nói project LÀM GÌ — dòng này leo lên L0
7
+ path: ~/AnhlpProjects/ten-project
8
+ updated: 2026-08-14
9
+ ---
10
+
11
+ # <Tên project>
12
+
13
+ ## Mục tiêu
14
+ <Một đoạn. Xong project này thì điều gì đúng mà hôm nay chưa đúng?>
15
+
16
+ ## Trạng thái hiện tại
17
+ <2–3 dòng. Đang ở đâu, vừa xong gì.>
18
+
19
+ ## Việc tiếp theo
20
+ 1. <việc cụ thể, làm được ngay>
21
+ 2. <việc kế>
22
+
23
+ ## Đang chặn
24
+ - <cái gì chặn, cần ai/cái gì để gỡ. Xoá mục này nếu không có.>
25
+
26
+ ## Stack & lệnh
27
+ | | |
28
+ |---|---|
29
+ | Stack | <ngôn ngữ, framework chính> |
30
+ | Chạy | `<lệnh dev>` |
31
+ | Test | `<lệnh test>` |
32
+ | Deploy | `<lệnh / nền tảng>` |
33
+
34
+ ## Người liên quan
35
+ - <tên> — <vai trò>. Chi tiết: `[[shared/people/ten]]`
36
+
37
+ ## Cạm bẫy đã biết
38
+ - <thứ đã cắn mình một lần rồi, đừng để cắn lần hai>
39
+
40
+ ## Quyết định
41
+ <Chỉ liệt kê, nội dung ở L2 — đừng chép vào đây.>
42
+ - [YYMMDD — tiêu đề](decisions/YYMMDD-slug.md)
43
+
44
+ ## Nhật ký
45
+ - [2026-08](log/2026-08.md)