archondev 2.19.29 → 2.19.30

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 (3) hide show
  1. package/README.md +2 -0
  2. package/dist/index.js +118 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -121,6 +121,8 @@ pnpm exec tsx scripts/init-governance-db.ts
121
121
  - Content-only requests (stories, outlines, lessons, visuals) use lightweight planning to avoid blocking.
122
122
  - BYOK shows per‑model usage and cost by today/week/month/year in `archon preferences` → “View usage details.”
123
123
  - You can paste multi‑line requests into interactive prompts; Archon captures them as a single response.
124
+ - Proposal approvals like `approve plan` now bind to the pending proposal context in chat mode.
125
+ - Governance boundary/path checks in execute now steer with actionable guidance and set atoms to `BLOCKED` rather than hard failing.
124
126
 
125
127
  **Tip:** Use `archon plan --edit` to adjust title and acceptance criteria before planning.
126
128
  **Web Checks:** If Archon detects a web project, it prompts to run A11y/SEO/GEO checks and stores your preference in `.archon/config.yaml`.
package/dist/index.js CHANGED
@@ -203,7 +203,7 @@ async function saveAtomEnvironmentState(atomId, cwd, state) {
203
203
  import chalk5 from "chalk";
204
204
  import readline from "readline";
205
205
  import { existsSync as existsSync6, readFileSync as readFileSync3, readdirSync as readdirSync3, appendFileSync } from "fs";
206
- import { join as join6 } from "path";
206
+ import { join as join6, relative } from "path";
207
207
 
208
208
  // src/core/context/manager.ts
209
209
  import { existsSync as existsSync2 } from "fs";
@@ -2616,6 +2616,8 @@ function detectWebProject(cwd) {
2616
2616
  // src/cli/start.ts
2617
2617
  var PAID_TIER_DEFAULT_CHAT_MODEL = "gemini-3.1-pro-preview";
2618
2618
  var pendingProposalRequest = null;
2619
+ var pendingProposalMode = "atom";
2620
+ var pendingAnalysisToAtomRequest = null;
2619
2621
  function uiText(rich, plain) {
2620
2622
  return isTerminalSafeMode() ? plain : rich;
2621
2623
  }
@@ -3676,11 +3678,21 @@ async function runAgentMode(cwd, state) {
3676
3678
  async function handleAgentConversationInput(cwd, input) {
3677
3679
  const normalized = input.trim().toLowerCase();
3678
3680
  if (!normalized) return false;
3681
+ if (pendingAnalysisToAtomRequest && isCreateAtomDirective(normalized)) {
3682
+ const request = pendingAnalysisToAtomRequest;
3683
+ pendingAnalysisToAtomRequest = null;
3684
+ console.log(chalk5.dim("\n> Great. Creating a governed task from the approved analysis plan.\n"));
3685
+ const { plan: plan3 } = await import("./plan-RHDFDSZE.js");
3686
+ await plan3(request, { conversational: true });
3687
+ await showLatestPlannedAtom(cwd);
3688
+ console.log(chalk5.dim('\nReply "execute atom" when you want implementation to start, or tell me what to change.'));
3689
+ return true;
3690
+ }
3679
3691
  if (pendingProposalRequest && isPlanApprovalDirective(normalized)) {
3680
3692
  await applyApprovedProposal(cwd);
3681
3693
  return true;
3682
3694
  }
3683
- if (isContinuationDirective(normalized)) {
3695
+ if (isExecutionDirective(normalized)) {
3684
3696
  await continueWithCurrentTask(cwd);
3685
3697
  return true;
3686
3698
  }
@@ -3706,7 +3718,7 @@ async function handleAgentConversationInput(cwd, input) {
3706
3718
  return true;
3707
3719
  }
3708
3720
  await showLatestPlannedAtom(cwd);
3709
- console.log(chalk5.dim('\nReply "continue" when you approve this plan, or tell me what to change.'));
3721
+ console.log(chalk5.dim('\nReply "execute atom" when you want implementation to start, or tell me what to change.'));
3710
3722
  return true;
3711
3723
  }
3712
3724
  function isReadOnlyExploreRequest(input) {
@@ -3733,6 +3745,7 @@ function wantsProposalBeforeExecution(input) {
3733
3745
  }
3734
3746
  async function showProposalForApproval(input) {
3735
3747
  pendingProposalRequest = input.trim();
3748
+ pendingProposalMode = shouldDoAnalysisBeforeAtom(input) ? "analysis" : "atom";
3736
3749
  console.log(chalk5.dim("\n> Understood. I will not create atoms yet.\n"));
3737
3750
  console.log(chalk5.bold("Proposed plan (for your approval):"));
3738
3751
  console.log(chalk5.dim(" 1. Review project files and locate the capsule markdown/source for day 1."));
@@ -3766,18 +3779,32 @@ Showing latest planned atom (${latest.externalId})...
3766
3779
  const { show: show2 } = await import("./show-7A3FACN6.js");
3767
3780
  await show2(latest.externalId);
3768
3781
  }
3769
- function isContinuationDirective(input) {
3782
+ function isPlanApprovalDirective(input) {
3770
3783
  const normalized = input.trim().toLowerCase();
3771
- return normalized === "continue" || normalized === "continue." || normalized === "go on" || normalized === "go ahead" || normalized === "next" || normalized === "proceed" || normalized === "do it";
3784
+ return normalized === "approve" || normalized === "approve plan" || normalized === "approved" || normalized === "yes" || normalized === "yes, proceed" || normalized === "proceed";
3772
3785
  }
3773
- function isPlanApprovalDirective(input) {
3786
+ function isExecutionDirective(input) {
3774
3787
  const normalized = input.trim().toLowerCase();
3775
- return normalized === "approve" || normalized === "approve plan" || normalized === "approved" || normalized === "yes" || normalized === "yes, proceed" || normalized === "proceed" || normalized === "continue" || normalized === "continue.";
3788
+ return normalized === "execute" || normalized === "execute atom" || normalized === "run atom" || normalized === "run it now" || normalized === "implement now" || normalized === "start execution" || normalized === "go ahead and execute";
3789
+ }
3790
+ function isCreateAtomDirective(input) {
3791
+ const normalized = input.trim().toLowerCase();
3792
+ return normalized === "create atom" || normalized === "make atom" || normalized === "save as atom" || normalized === "turn this into a task" || normalized === "create task";
3793
+ }
3794
+ function shouldDoAnalysisBeforeAtom(input) {
3795
+ const normalized = input.toLowerCase();
3796
+ const hasAnalysisIntent = /\b(review|analy[sz]e|inspect|read)\b/.test(normalized) && /\b(files?|folder|project|markdown|lesson|capsule)\b/.test(normalized);
3797
+ const asksForPlan = /\b(let me know|what your plan is|plan is|how to implement|before creating|before you implement)\b/.test(normalized);
3798
+ return hasAnalysisIntent && asksForPlan;
3776
3799
  }
3777
3800
  async function applyApprovedProposal(cwd) {
3778
3801
  const approvedRequest = pendingProposalRequest;
3779
3802
  if (!approvedRequest) return;
3780
3803
  pendingProposalRequest = null;
3804
+ if (pendingProposalMode === "analysis") {
3805
+ await provideAnalysisFirstPlan(cwd, approvedRequest);
3806
+ return;
3807
+ }
3781
3808
  console.log(chalk5.dim("\n> Great. I will create the task from your approved request.\n"));
3782
3809
  const { plan: plan2 } = await import("./plan-RHDFDSZE.js");
3783
3810
  await plan2(approvedRequest, { conversational: true });
@@ -3786,7 +3813,89 @@ async function applyApprovedProposal(cwd) {
3786
3813
  return;
3787
3814
  }
3788
3815
  await showLatestPlannedAtom(cwd);
3789
- console.log(chalk5.dim('\nReply "continue" when you approve this plan, or tell me what to change.'));
3816
+ console.log(chalk5.dim('\nReply "execute atom" when you want implementation to start, or tell me what to change.'));
3817
+ }
3818
+ async function provideAnalysisFirstPlan(cwd, request) {
3819
+ console.log(chalk5.dim("\n> Great. I will analyze first and return a concrete recommendation before creating atoms.\n"));
3820
+ const markdownFiles = collectMarkdownFiles(cwd);
3821
+ const dayOneCandidates = markdownFiles.filter((file) => /(day[-_ ]?0?1|lesson[-_ ]?0?1|first)/i.test(file));
3822
+ const capsuleCandidates = markdownFiles.filter((file) => /capsule/i.test(file));
3823
+ const primaryCandidates = dayOneCandidates.length > 0 ? dayOneCandidates : capsuleCandidates;
3824
+ const reviewedFiles = primaryCandidates.slice(0, 8);
3825
+ const stats = computeContentStats(cwd, reviewedFiles);
3826
+ const recommendSplit = stats.totalWords > 900 || stats.totalHeadings >= 6;
3827
+ const capsuleCount = recommendSplit ? 2 : 1;
3828
+ console.log(chalk5.bold("Analysis summary (no atoms created):"));
3829
+ if (reviewedFiles.length === 0) {
3830
+ console.log(chalk5.dim(" - I did not find obvious day-1 capsule markdown files automatically."));
3831
+ console.log(chalk5.dim(" - Share the target file path and I will evaluate it directly."));
3832
+ } else {
3833
+ console.log(chalk5.dim(" Files reviewed:"));
3834
+ for (const file of reviewedFiles) {
3835
+ console.log(chalk5.dim(` - ${file}`));
3836
+ }
3837
+ console.log(chalk5.dim(` Content signals: ~${stats.totalWords} words, ${stats.totalHeadings} section headings.`));
3838
+ }
3839
+ console.log();
3840
+ console.log(chalk5.bold("Recommendation:"));
3841
+ if (reviewedFiles.length === 0) {
3842
+ console.log(chalk5.dim(" I need the exact day-1 capsule file path to make a reliable one-vs-many capsule recommendation."));
3843
+ } else if (recommendSplit) {
3844
+ console.log(chalk5.dim(` Split day 1 into ${capsuleCount} capsules for clarity and lower cognitive load.`));
3845
+ } else {
3846
+ console.log(chalk5.dim(" Keep day 1 as one capsule to preserve continuity and reduce context switching."));
3847
+ }
3848
+ console.log();
3849
+ console.log(chalk5.bold("Implementation plan (proposed):"));
3850
+ console.log(chalk5.dim(" 1. Confirm target source file(s) for day 1 content."));
3851
+ console.log(chalk5.dim(` 2. Define capsule boundaries and titles (${capsuleCount} capsule${capsuleCount > 1 ? "s" : ""}).`));
3852
+ console.log(chalk5.dim(" 3. Draft capsule markdown(s) with consistent template and learning objective per capsule."));
3853
+ console.log(chalk5.dim(" 4. Add index/manifest links and quick validation checks for user flow."));
3854
+ pendingAnalysisToAtomRequest = request;
3855
+ console.log();
3856
+ console.log(chalk5.dim('Reply "create atom" to save this as a governed task, or tell me what to adjust first.'));
3857
+ }
3858
+ function collectMarkdownFiles(cwd) {
3859
+ const results = [];
3860
+ const queue = ["."];
3861
+ const skipDirs = /* @__PURE__ */ new Set([".git", "node_modules", ".archon", "dist", "build", ".next", ".turbo"]);
3862
+ while (queue.length > 0 && results.length < 200) {
3863
+ const current = queue.shift();
3864
+ if (!current) break;
3865
+ const abs = join6(cwd, current);
3866
+ let entries;
3867
+ try {
3868
+ entries = readdirSync3(abs, { withFileTypes: true });
3869
+ } catch {
3870
+ continue;
3871
+ }
3872
+ for (const entry of entries) {
3873
+ const rel = current === "." ? entry.name : join6(current, entry.name);
3874
+ if (entry.isDirectory()) {
3875
+ if (!skipDirs.has(entry.name)) {
3876
+ queue.push(rel);
3877
+ }
3878
+ continue;
3879
+ }
3880
+ if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
3881
+ results.push(relative(cwd, join6(cwd, rel)).replace(/\\/g, "/"));
3882
+ }
3883
+ }
3884
+ }
3885
+ return results;
3886
+ }
3887
+ function computeContentStats(cwd, files) {
3888
+ let totalWords = 0;
3889
+ let totalHeadings = 0;
3890
+ for (const relPath of files) {
3891
+ try {
3892
+ const content = readFileSync3(join6(cwd, relPath), "utf-8");
3893
+ totalWords += content.split(/\s+/).filter(Boolean).length;
3894
+ totalHeadings += content.split("\n").filter((line) => line.trim().startsWith("#")).length;
3895
+ } catch {
3896
+ }
3897
+ }
3898
+ return { totalWords, totalHeadings };
3790
3899
  }
3791
3900
  async function continueWithCurrentTask(cwd) {
3792
3901
  const { listLocalAtoms: listLocalAtoms2 } = await import("./plan-RHDFDSZE.js");
@@ -3944,7 +4053,7 @@ async function handlePostExploreAction(cwd, request, options = {}) {
3944
4053
  return;
3945
4054
  }
3946
4055
  await showLatestPlannedAtom(cwd);
3947
- console.log(chalk5.dim('\nReply "continue" when you approve this plan, or tell me what to change.'));
4056
+ console.log(chalk5.dim('\nReply "execute atom" when you want implementation to start, or tell me what to change.'));
3948
4057
  }
3949
4058
  }
3950
4059
  async function planTask() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archondev",
3
- "version": "2.19.29",
3
+ "version": "2.19.30",
4
4
  "description": "Local-first AI-powered development governance system",
5
5
  "main": "dist/index.js",
6
6
  "bin": {