autonomous-sdlc-harness 0.1.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/LICENSE +201 -0
- package/NOTICE +7 -0
- package/README.md +24 -0
- package/dist/cli.js +194 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.js +561 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/daemon.js +791 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/doctor.js +336 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.js +2023 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/registry.js +42 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/config/check.js +505 -0
- package/dist/config/check.js.map +1 -0
- package/dist/config/io.js +177 -0
- package/dist/config/io.js.map +1 -0
- package/dist/config/model.js +406 -0
- package/dist/config/model.js.map +1 -0
- package/dist/core/errors.js +71 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/git.js +537 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/json.js +125 -0
- package/dist/core/json.js.map +1 -0
- package/dist/core/layerCoverage.js +141 -0
- package/dist/core/layerCoverage.js.map +1 -0
- package/dist/core/layerGapRemedy.js +62 -0
- package/dist/core/layerGapRemedy.js.map +1 -0
- package/dist/core/nameList.js +23 -0
- package/dist/core/nameList.js.map +1 -0
- package/dist/core/paths.js +153 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/prompt.js +206 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/repoPaths.js +55 -0
- package/dist/core/repoPaths.js.map +1 -0
- package/dist/core/report.js +150 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/templating.js +88 -0
- package/dist/core/templating.js.map +1 -0
- package/dist/core/writer.js +479 -0
- package/dist/core/writer.js.map +1 -0
- package/dist/daemon/backend.js +180 -0
- package/dist/daemon/backend.js.map +1 -0
- package/dist/daemon/units.js +380 -0
- package/dist/daemon/units.js.map +1 -0
- package/dist/detect/nestedApplication.js +79 -0
- package/dist/detect/nestedApplication.js.map +1 -0
- package/dist/detect/presets.js +2033 -0
- package/dist/detect/presets.js.map +1 -0
- package/dist/detect/signals.js +1368 -0
- package/dist/detect/signals.js.map +1 -0
- package/dist/doctor/checks.js +3530 -0
- package/dist/doctor/checks.js.map +1 -0
- package/dist/generators/claudeContext.js +588 -0
- package/dist/generators/claudeContext.js.map +1 -0
- package/dist/generators/githooks.js +446 -0
- package/dist/generators/githooks.js.map +1 -0
- package/dist/generators/harnessConfig.js +632 -0
- package/dist/generators/harnessConfig.js.map +1 -0
- package/dist/generators/notifications.js +191 -0
- package/dist/generators/notifications.js.map +1 -0
- package/dist/generators/outerLoopScripts.js +165 -0
- package/dist/generators/outerLoopScripts.js.map +1 -0
- package/dist/generators/permissionProfile.js +1172 -0
- package/dist/generators/permissionProfile.js.map +1 -0
- package/dist/generators/projectSettings.js +322 -0
- package/dist/generators/projectSettings.js.map +1 -0
- package/dist/generators/repoRoot.js +417 -0
- package/dist/generators/repoRoot.js.map +1 -0
- package/dist/generators/scripts.js +557 -0
- package/dist/generators/scripts.js.map +1 -0
- package/dist/generators/stateDir.js +221 -0
- package/dist/generators/stateDir.js.map +1 -0
- package/dist/machine/paths.js +111 -0
- package/dist/machine/paths.js.map +1 -0
- package/dist/machine/plugins.js +224 -0
- package/dist/machine/plugins.js.map +1 -0
- package/dist/machine/registry.js +330 -0
- package/dist/machine/registry.js.map +1 -0
- package/package.json +23 -0
- package/scripts/README.md +13 -0
- package/scripts/daemon/launchd.plist.template +59 -0
- package/scripts/daemon/systemd.service.template +58 -0
- package/templates/README.md +15 -0
- package/templates/claude/CLAUDE.md +54 -0
- package/templates/claude/README.md +5 -0
- package/templates/claude/context/api.md +29 -0
- package/templates/claude/context/conventions.md +23 -0
- package/templates/claude/context/data-layer.md +28 -0
- package/templates/claude/context/data-storage.md +29 -0
- package/templates/claude/context/docs-catalog.md +29 -0
- package/templates/claude/context/domain.md +28 -0
- package/templates/claude/context/layer.md +20 -0
- package/templates/claude/context/module.md +30 -0
- package/templates/claude/context/package.md +29 -0
- package/templates/claude/context/presentation.md +32 -0
- package/templates/claude/context/state-slices.md +28 -0
- package/templates/claude/context/tests.md +28 -0
- package/templates/claude/harness-task-offer.md +58 -0
- package/templates/claude/push-notify.env.example +21 -0
- package/templates/claude/qa-accounts.env.example +38 -0
- package/templates/claude/qa_test_scenarios.md +110 -0
- package/templates/claude/settings.autonomous.json +93 -0
- package/templates/claude/settings.autonomous.qa.json +36 -0
- package/templates/githooks/README.md +3 -0
- package/templates/githooks/pre-push +72 -0
- package/templates/repo/README.md +3 -0
- package/templates/repo/gitattributes +16 -0
- package/templates/repo/gitignore +61 -0
- package/templates/repo/gitignore.qa +25 -0
- package/templates/repo/mcp.json +17 -0
- package/templates/scripts/README.md +5 -0
- package/templates/scripts/autonomous-format-stream.sh +95 -0
- package/templates/scripts/autonomous-notify.sh +337 -0
- package/templates/scripts/autonomous-watcher.sh +3087 -0
- package/templates/scripts/cleanup-merged-worktrees.sh +327 -0
- package/templates/scripts/commit-on-branch.sh +288 -0
- package/templates/scripts/create-worktree.sh +360 -0
- package/templates/scripts/deploy.sh +47 -0
- package/templates/scripts/lib/harness-run-lib.sh +1481 -0
- package/templates/scripts/push-branch.sh +140 -0
- package/templates/scripts/refresh-branch.sh +244 -0
- package/templates/scripts/restart-watcher.sh +401 -0
- package/templates/scripts/scratch-run.sh +302 -0
- package/templates/scripts/setup-worktree.sh +262 -0
- package/templates/scripts/start-dev-server.sh +99 -0
- package/templates/scripts/test.sh +50 -0
- package/templates/scripts/typecheck.sh +50 -0
- package/templates/state-dir/README-root.md +13 -0
- package/templates/state-dir/README.md +9 -0
- package/templates/state-dir/architecture_branch_review_point_reviews/README.md +9 -0
- package/templates/state-dir/architecture_branch_reviews/README.md +9 -0
- package/templates/state-dir/architecture_reviews/README.md +9 -0
- package/templates/state-dir/architecture_user_review_reviews/README.md +9 -0
- package/templates/state-dir/autonomous_inbox/README.md +9 -0
- package/templates/state-dir/autonomous_logs/README.md +9 -0
- package/templates/state-dir/branch_statistics/README.md +9 -0
- package/templates/state-dir/business_parity_branch_review_point_reviews/README.md +9 -0
- package/templates/state-dir/business_parity_branch_reviews/README.md +9 -0
- package/templates/state-dir/business_parity_reviews/README.md +9 -0
- package/templates/state-dir/business_parity_user_review_reviews/README.md +9 -0
- package/templates/state-dir/clarification_digests/README.md +9 -0
- package/templates/state-dir/clarifications/README.md +9 -0
- package/templates/state-dir/code_reviews/README.md +9 -0
- package/templates/state-dir/dispatch_additions/README.md +19 -0
- package/templates/state-dir/docs_catalog/README.md +9 -0
- package/templates/state-dir/flow_progress/README.md +9 -0
- package/templates/state-dir/improvement_observations/README.md +19 -0
- package/templates/state-dir/improvement_suggestions.md +29 -0
- package/templates/state-dir/lessons.md +23 -0
- package/templates/state-dir/qa_review_point_reviews/README.md +9 -0
- package/templates/state-dir/qa_reviews/README.md +9 -0
- package/templates/state-dir/review_plan_point_reviews/README.md +9 -0
- package/templates/state-dir/review_plan_reviews/README.md +9 -0
- package/templates/state-dir/scratch/README.md +11 -0
- package/templates/state-dir/skeptic_review_plan_reviews/README.md +9 -0
- package/templates/state-dir/skeptic_review_point_reviews/README.md +9 -0
- package/templates/state-dir/skeptic_reviews/README.md +9 -0
- package/templates/state-dir/story_plans/README.md +9 -0
- package/templates/state-dir/task_plan_point_reviews/README.md +9 -0
- package/templates/state-dir/task_plan_reviews/README.md +9 -0
- package/templates/state-dir/task_plans/README.md +9 -0
- package/templates/state-dir/task_prompts/README.md +9 -0
- package/templates/state-dir/ui_test_plan_reviews/README.md +9 -0
- package/templates/state-dir/ui_test_plans/README.md +9 -0
- package/templates/state-dir/user_review_fix_plan_point_reviews/README.md +9 -0
- package/templates/state-dir/user_reviews/README.md +9 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generator: the run-artifact tree `init` materialises at the configured `stateDir`.
|
|
3
|
+
*
|
|
4
|
+
* **The rule this module exists to enforce: {@link STATE_DIR_ENTRIES} is the single declaration of
|
|
5
|
+
* which directories the tree has and which phase each one belongs to.** One row is one line, so a
|
|
6
|
+
* directory is added or gated by editing a row rather than by threading a condition through the
|
|
7
|
+
* writer, and `doctor` asks {@link selectedStateDirs} what should be there instead of re-deriving
|
|
8
|
+
* the list from its own copy. A second copy of this table anywhere is a copy that will drift.
|
|
9
|
+
*
|
|
10
|
+
* ## Three non-obvious choices, and where each comes from
|
|
11
|
+
*
|
|
12
|
+
* 1. **A phase that is off gets no directories at all.** An adopter who never runs the parity phase
|
|
13
|
+
* has no reference implementation to compare against, so a `business_parity_*` directory in
|
|
14
|
+
* their tree would be an empty slot they cannot fill and cannot tell from one the flow forgot to
|
|
15
|
+
* write. Turning the phase on in `harness.config.json` and re-running `init` creates them, which
|
|
16
|
+
* is why the omission is safe as well as tidy.
|
|
17
|
+
* 2. **The two ledgers are `create-if-absent` and are never truncated or rewritten.** They
|
|
18
|
+
* accumulate over a project's whole life and are the only copy of that history — the write
|
|
19
|
+
* engine's table says the same (`core/writer.ts`) — so a re-run leaves a ledger with content in
|
|
20
|
+
* it byte-identical. This is the artifact where the re-run contract is not a nicety: an
|
|
21
|
+
* overwrite here destroys a record nothing else holds.
|
|
22
|
+
* 3. **Templates are copied verbatim; there is no token substitution in this generator.** The
|
|
23
|
+
* wrapper-script generator renders `{{token}}`s because a wrapper's body is a *detected* value;
|
|
24
|
+
* nothing in this tree depends on one, and a README that named its own configured path would go
|
|
25
|
+
* stale the moment the adopter moved the tree. A path a reader wants is read from the config.
|
|
26
|
+
*
|
|
27
|
+
* ## Scope this release
|
|
28
|
+
*
|
|
29
|
+
* This module is the **writer**, and what it writes out is a contract per directory: each README
|
|
30
|
+
* states what one file in its directory is and exactly how it is named, who writes it and who reads
|
|
31
|
+
* it by role, when it appears and when it is superseded, and the one mistake a reader would
|
|
32
|
+
* otherwise make. The tree's root README frames the set, and the two ledgers carry a worked example
|
|
33
|
+
* entry each. Every row here has a file at its final path and name, so revising a contract is an
|
|
34
|
+
* edit in place rather than a second layout.
|
|
35
|
+
*
|
|
36
|
+
* Four of this tree's directories are runtime surfaces whose *contents* are machine-local — the two
|
|
37
|
+
* the run daemon writes, the park-and-ask clarification channel, and the scratch directory an agent
|
|
38
|
+
* writes a probe file into — but which of them an ignore rule
|
|
39
|
+
* may cover is settled in the generator that writes the repository's ignore file
|
|
40
|
+
* (`generators/repoRoot.ts`), not here: a directory with a row in {@link STATE_DIR_ENTRIES} has a
|
|
41
|
+
* committed README and is therefore ignored **by its contents** with a negation for that file, because
|
|
42
|
+
* a whole-directory rule takes the README with it and no negation can bring it back. That generator
|
|
43
|
+
* reads this table to check the row still exists, so a directory removed on either side fails loudly
|
|
44
|
+
* rather than leaving a rule pointing at nothing.
|
|
45
|
+
*/
|
|
46
|
+
import { join } from 'node:path';
|
|
47
|
+
import { DEFAULTS, STATE_DIR_DOT_PATTERN } from '../config/model.js';
|
|
48
|
+
import { HarnessError } from '../core/errors.js';
|
|
49
|
+
import { readTemplate } from '../core/paths.js';
|
|
50
|
+
import { normalizeRepoDir } from '../core/repoPaths.js';
|
|
51
|
+
/**
|
|
52
|
+
* Every directory of the run-artifact tree, in the order `init` writes it: the always-on set
|
|
53
|
+
* first, then the phase-gated ones grouped by phase.
|
|
54
|
+
*
|
|
55
|
+
* The set comes from the artifact families the flow actually writes — one directory per family,
|
|
56
|
+
* with the per-item findings directories kept separate from the reviews they re-check, because the
|
|
57
|
+
* two have different writers and different readers.
|
|
58
|
+
*/
|
|
59
|
+
export const STATE_DIR_ENTRIES = Object.freeze([
|
|
60
|
+
// Always written.
|
|
61
|
+
Object.freeze({ dir: 'task_prompts' }),
|
|
62
|
+
Object.freeze({ dir: 'story_plans' }),
|
|
63
|
+
Object.freeze({ dir: 'task_plans' }),
|
|
64
|
+
Object.freeze({ dir: 'code_reviews' }),
|
|
65
|
+
Object.freeze({ dir: 'task_plan_reviews' }),
|
|
66
|
+
Object.freeze({ dir: 'task_plan_point_reviews' }),
|
|
67
|
+
Object.freeze({ dir: 'review_plan_reviews' }),
|
|
68
|
+
Object.freeze({ dir: 'review_plan_point_reviews' }),
|
|
69
|
+
Object.freeze({ dir: 'skeptic_reviews' }),
|
|
70
|
+
Object.freeze({ dir: 'skeptic_review_plan_reviews' }),
|
|
71
|
+
Object.freeze({ dir: 'skeptic_review_point_reviews' }),
|
|
72
|
+
Object.freeze({ dir: 'architecture_reviews' }),
|
|
73
|
+
Object.freeze({ dir: 'architecture_branch_reviews' }),
|
|
74
|
+
Object.freeze({ dir: 'architecture_branch_review_point_reviews' }),
|
|
75
|
+
Object.freeze({ dir: 'architecture_user_review_reviews' }),
|
|
76
|
+
Object.freeze({ dir: 'user_reviews' }),
|
|
77
|
+
Object.freeze({ dir: 'user_review_fix_plan_point_reviews' }),
|
|
78
|
+
Object.freeze({ dir: 'flow_progress' }),
|
|
79
|
+
Object.freeze({ dir: 'branch_statistics' }),
|
|
80
|
+
Object.freeze({ dir: 'autonomous_inbox' }),
|
|
81
|
+
Object.freeze({ dir: 'autonomous_logs' }),
|
|
82
|
+
Object.freeze({ dir: 'clarifications' }),
|
|
83
|
+
// The fourth of the runtime surfaces whose contents are machine-local, and the reason it sits with
|
|
84
|
+
// the three above rather than under a phase: an implementer or a reviewer in *any* phase may need
|
|
85
|
+
// to run a probe, so gating it would take the capability away from the phases that ask for it most.
|
|
86
|
+
Object.freeze({ dir: 'scratch' }),
|
|
87
|
+
Object.freeze({ dir: 'improvement_observations' }),
|
|
88
|
+
Object.freeze({ dir: 'clarification_digests' }),
|
|
89
|
+
Object.freeze({ dir: 'dispatch_additions' }),
|
|
90
|
+
// The interactive-test phase.
|
|
91
|
+
Object.freeze({ dir: 'qa_reviews', phase: 'qa' }),
|
|
92
|
+
Object.freeze({ dir: 'qa_review_point_reviews', phase: 'qa' }),
|
|
93
|
+
Object.freeze({ dir: 'ui_test_plans', phase: 'qa' }),
|
|
94
|
+
Object.freeze({ dir: 'ui_test_plan_reviews', phase: 'qa' }),
|
|
95
|
+
// The documentation phase.
|
|
96
|
+
Object.freeze({ dir: 'docs_catalog', phase: 'docs' }),
|
|
97
|
+
// The reference-implementation-parity phase.
|
|
98
|
+
Object.freeze({ dir: 'business_parity_reviews', phase: 'parity' }),
|
|
99
|
+
Object.freeze({ dir: 'business_parity_branch_reviews', phase: 'parity' }),
|
|
100
|
+
Object.freeze({ dir: 'business_parity_branch_review_point_reviews', phase: 'parity' }),
|
|
101
|
+
Object.freeze({ dir: 'business_parity_user_review_reviews', phase: 'parity' }),
|
|
102
|
+
]);
|
|
103
|
+
/**
|
|
104
|
+
* The long-lived ledgers at the root of the tree, which are files rather than directories and are
|
|
105
|
+
* written whatever the phases are.
|
|
106
|
+
*/
|
|
107
|
+
export const STATE_DIR_LEDGERS = Object.freeze(['lessons.md', 'improvement_suggestions.md']);
|
|
108
|
+
/** The templates' subdirectory under `cli/templates/`, addressed as {@link readTemplate} wants it. */
|
|
109
|
+
const TEMPLATE_DIR = 'state-dir';
|
|
110
|
+
/** The per-directory contract file, in the template tree and in the adopter's tree alike. */
|
|
111
|
+
const README_FILENAME = 'README.md';
|
|
112
|
+
/**
|
|
113
|
+
* The template for the tree's own top-level README.
|
|
114
|
+
*
|
|
115
|
+
* Named apart from the per-directory ones because `templates/state-dir/README.md` already describes
|
|
116
|
+
* the *template* directory to a reader of this repository, and is not written to an adopter.
|
|
117
|
+
*/
|
|
118
|
+
const ROOT_README_TEMPLATE = 'README-root.md';
|
|
119
|
+
/**
|
|
120
|
+
* Re-assert the two things `stateDir` must be before a single directory is enqueued under it.
|
|
121
|
+
*
|
|
122
|
+
* The schema checks both and so does `config/check.ts`, and this checks them **again** deliberately:
|
|
123
|
+
* a config can be hand-edited between the run that validated it and this one, and by the time a
|
|
124
|
+
* dot-named tree has been created the failure is invisible — an unattended run may not be permitted
|
|
125
|
+
* to write beneath a dot-path at all, and it reports success with no artifacts to show for it. That is a condition the adopter fixes by editing one key, so it carries the default
|
|
126
|
+
* exit code rather than the internal one.
|
|
127
|
+
*
|
|
128
|
+
* The check runs against the **raw** configured value rather than the normalised one: normalising
|
|
129
|
+
* strips a leading `./`, which is itself a dot segment the rule refuses.
|
|
130
|
+
*/
|
|
131
|
+
function assertWritableStateDir(raw) {
|
|
132
|
+
if (STATE_DIR_DOT_PATTERN.test(raw)) {
|
|
133
|
+
throw new HarnessError(`stateDir is ${JSON.stringify(raw)}, which has a path segment starting with '.': the run-artifact tree must not be dot-named or reach through a dot segment, because it has to be writable by an unattended run and a dot-path is where a host reserves directories an unattended run may not write to — measured for .claude/**, where such a run completes with exit 0 having written nothing. Set stateDir in harness.config.json to a name without a leading dot; do not "fix" it back to a dot-name`);
|
|
134
|
+
}
|
|
135
|
+
const normalized = normalizeRepoDir(raw.trim());
|
|
136
|
+
if (normalized === '.' || normalized === '') {
|
|
137
|
+
throw new HarnessError(`stateDir is ${JSON.stringify(raw)}, which names the repository root rather than a directory in it: the run-artifact tree is a directory of its own, and writing it at the root would scatter the flow's artifact directories through the repository. Set stateDir in harness.config.json to a directory name`);
|
|
138
|
+
}
|
|
139
|
+
return normalized;
|
|
140
|
+
}
|
|
141
|
+
/** Whether a phase is on. Absent phases are off, which is the schema's default for all three. */
|
|
142
|
+
function phaseEnabled(phases, phase) {
|
|
143
|
+
return phases?.[phase] === true;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The directories that belong in the tree for a given set of phases, in table order.
|
|
147
|
+
*
|
|
148
|
+
* Exported so `doctor` reports a missing directory against the same list `init` writes, rather than
|
|
149
|
+
* against a second copy of it that can disagree.
|
|
150
|
+
*/
|
|
151
|
+
export function selectedStateDirs(phases) {
|
|
152
|
+
return STATE_DIR_ENTRIES.filter((entry) => entry.phase === undefined || phaseEnabled(phases, entry.phase));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Enqueue the run-artifact tree: `ensure-dir` for the root and for every selected directory, and
|
|
156
|
+
* `create-if-absent` for the tree's README, each directory's README and both ledgers.
|
|
157
|
+
*
|
|
158
|
+
* `ensure-dir` is the whole of the directory contract — it is idempotent by construction, so a
|
|
159
|
+
* second `init` reports the tree as ensured and changes nothing, and it never removes a directory
|
|
160
|
+
* an adopter added. `create-if-absent` is the contract for every file here: the READMEs because the
|
|
161
|
+
* adopter may have rewritten a contract sentence to match how their team uses the directory, and
|
|
162
|
+
* the ledgers because they accumulate and an overwrite would destroy history.
|
|
163
|
+
*
|
|
164
|
+
* The two differ in what `--force` may do to them. A README is template content, so `--force`
|
|
165
|
+
* replaces one after the write engine has taken a `.bak`, and the adopter's sentence is a `.bak`
|
|
166
|
+
* away. **The ledgers are outside `--force` entirely** (`forceOverride: 'never'`): nothing can
|
|
167
|
+
* re-derive an accumulating ledger, and the engine's `.bak` is single-generation — it is
|
|
168
|
+
* overwritten rather than chained — so "recoverable" would only ever have meant "recoverable until
|
|
169
|
+
* the next forced run". `--force` is an operation this CLI's own remedy messages routinely send an
|
|
170
|
+
* adopter to, so a ledger has to survive it however many times it is run.
|
|
171
|
+
*
|
|
172
|
+
* Nothing here touches the filesystem: the generator plans, and `init` applies the plan once.
|
|
173
|
+
*/
|
|
174
|
+
export function writeStateDir({ repoRoot, config, plan }) {
|
|
175
|
+
const stateDir = assertWritableStateDir(config.stateDir ?? DEFAULTS.stateDir);
|
|
176
|
+
const root = join(repoRoot, stateDir);
|
|
177
|
+
const directories = [];
|
|
178
|
+
const skipped = [];
|
|
179
|
+
const notes = [];
|
|
180
|
+
plan.add({ path: root, policy: 'ensure-dir', label: 'run-artifact tree' });
|
|
181
|
+
plan.add({
|
|
182
|
+
path: join(root, README_FILENAME),
|
|
183
|
+
policy: 'create-if-absent',
|
|
184
|
+
content: readTemplate(`${TEMPLATE_DIR}/${ROOT_README_TEMPLATE}`),
|
|
185
|
+
label: 'run-artifact tree README',
|
|
186
|
+
});
|
|
187
|
+
for (const ledger of STATE_DIR_LEDGERS) {
|
|
188
|
+
plan.add({
|
|
189
|
+
path: join(root, ledger),
|
|
190
|
+
policy: 'create-if-absent',
|
|
191
|
+
content: readTemplate(`${TEMPLATE_DIR}/${ledger}`),
|
|
192
|
+
label: `ledger ${ledger}`,
|
|
193
|
+
// Outside `--force` entirely (`core/writer.ts`'s `WriteRequest.forceOverride`), and for the
|
|
194
|
+
// same reason `harness.config.json` is: an accumulating ledger has no derivable content to
|
|
195
|
+
// regenerate, so an overwrite is pure loss — and the engine's `.bak` is single-generation, so
|
|
196
|
+
// a second forced run takes the history with it. `--force` is an operation this CLI's own
|
|
197
|
+
// remedy messages send an adopter to, so it has to be safe to run twice.
|
|
198
|
+
forceOverride: 'never',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
for (const entry of STATE_DIR_ENTRIES) {
|
|
202
|
+
const relative = `${stateDir}/${entry.dir}`;
|
|
203
|
+
if (entry.phase !== undefined && !phaseEnabled(config.phases, entry.phase)) {
|
|
204
|
+
skipped.push(relative);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
plan.add({ path: join(root, entry.dir), policy: 'ensure-dir', label: `artifact directory ${entry.dir}` });
|
|
208
|
+
plan.add({
|
|
209
|
+
path: join(root, entry.dir, README_FILENAME),
|
|
210
|
+
policy: 'create-if-absent',
|
|
211
|
+
content: readTemplate(`${TEMPLATE_DIR}/${entry.dir}/${README_FILENAME}`),
|
|
212
|
+
label: `artifact directory README ${entry.dir}`,
|
|
213
|
+
});
|
|
214
|
+
directories.push(relative);
|
|
215
|
+
}
|
|
216
|
+
if (skipped.length > 0) {
|
|
217
|
+
notes.push(`${skipped.length} artifact ${skipped.length === 1 ? 'directory belongs' : 'directories belong'} to a phase that is off, so ${skipped.length === 1 ? 'it was' : 'they were'} not created: turn the phase on in harness.config.json and re-run init to add ${skipped.length === 1 ? 'it' : 'them'}`);
|
|
218
|
+
}
|
|
219
|
+
return { stateDir, root, directories, skipped, notes };
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=stateDir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateDir.js","sourceRoot":"","sources":["../../src/generators/stateDir.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The machine-local directories this CLI addresses, resolved once: its own two, and the agent
|
|
3
|
+
* runner's configuration home.
|
|
4
|
+
*
|
|
5
|
+
* **The rule this module exists to enforce: the environment variables that move any of them are read
|
|
6
|
+
* here and nowhere else in the CLI.**
|
|
7
|
+
* `grep -rn 'XDG_STATE_HOME\|XDG_CONFIG_HOME\|CLAUDE_CONFIG_DIR' cli/src` answers with this file
|
|
8
|
+
* alone, which is the property that makes the resolution correctable in one place — a second reader
|
|
9
|
+
* would be a second answer, and the two would disagree exactly on the machines whose environment is
|
|
10
|
+
* not the default one.
|
|
11
|
+
*
|
|
12
|
+
* ## Three things this module is careful about
|
|
13
|
+
*
|
|
14
|
+
* 1. **It addresses the usage lane's directory; it never re-defines it.** {@link machineStateDir}
|
|
15
|
+
* is the same directory `docs/watcher.md` §5 publishes as the home of `usage-state.json` and
|
|
16
|
+
* `run-lane.lock/`, and that section is the format of record for both artifacts. Nothing here
|
|
17
|
+
* describes their shape, their merge rule or their lock protocol: a TypeScript consumer reads
|
|
18
|
+
* them there, and this module only says where the directory is.
|
|
19
|
+
* 2. **The resolution matches the shell half byte for byte**, because the two halves address one
|
|
20
|
+
* directory on one machine: `hr_lane_dir()` in the generated `lib/harness-run-lib.sh` reads
|
|
21
|
+
* `XDG_STATE_HOME`, falls back to `$HOME/.local/state` when it is **unset or empty**, strips one
|
|
22
|
+
* trailing slash, and appends {@link MACHINE_DIR_NAME}. So does {@link resolveMachineDir}, with
|
|
23
|
+
* `homeRoot()` — `core/paths.ts`'s single definition of the account home — standing in for
|
|
24
|
+
* `$HOME`. A variable holding a relative path yields a relative directory here exactly as it
|
|
25
|
+
* does there. Only a target on a `WritePlan` is refused for that, and of these two directories
|
|
26
|
+
* the one that goes on a plan is {@link machineConfigDir} — `generators/notifications.ts`
|
|
27
|
+
* enqueues it under `allowOutsideRepo`. `machine/registry.ts` writes {@link machineStateDir}
|
|
28
|
+
* directly, so a relative base there lands under the process's working directory.
|
|
29
|
+
* 3. **A read never creates any of these directories.** Every function here is pure string
|
|
30
|
+
* resolution — nothing touches the filesystem — so a `doctor` check or any other reader can ask
|
|
31
|
+
* where a file would be without bringing its directory into existence. Creation belongs to a
|
|
32
|
+
* writer, and a writer creates it at {@link MACHINE_DIR_MODE}: these directories hold an
|
|
33
|
+
* account's push credential and its cross-repository run state, neither of which is another
|
|
34
|
+
* account's to read. {@link claudeHome} is the one directory here **no** part of this CLI
|
|
35
|
+
* creates or writes into at all — it is the agent runner's, and this CLI only reads from it.
|
|
36
|
+
*/
|
|
37
|
+
import { homeRoot } from '../core/paths.js';
|
|
38
|
+
/**
|
|
39
|
+
* The one directory name both machine-local trees carry, under whichever base applies.
|
|
40
|
+
*
|
|
41
|
+
* It is the package name, and it is shared with the shell half's `hr_lane_dir`. Changing it here
|
|
42
|
+
* without changing it there would leave two components each believing they hold the machine.
|
|
43
|
+
*/
|
|
44
|
+
export const MACHINE_DIR_NAME = 'autonomous-sdlc-harness';
|
|
45
|
+
/**
|
|
46
|
+
* The mode a writer creates either directory at: the owner's, and nobody else's.
|
|
47
|
+
*
|
|
48
|
+
* The same `0700` the shell half applies with an explicit `chmod` after its `mkdir -p`, and for
|
|
49
|
+
* the same reason — the mode argument of a directory creation is masked by the process umask and
|
|
50
|
+
* does nothing at all for a directory that already exists, so it is applied as its own step.
|
|
51
|
+
*/
|
|
52
|
+
export const MACHINE_DIR_MODE = 0o700;
|
|
53
|
+
/**
|
|
54
|
+
* `<base>/autonomous-sdlc-harness` for one XDG base variable and its fallback, in the shell half's
|
|
55
|
+
* exact order: the variable when it holds something, the fallback under {@link homeRoot} otherwise,
|
|
56
|
+
* then one trailing slash stripped.
|
|
57
|
+
*
|
|
58
|
+
* The result is joined with a literal separator rather than through `path.join`, which would
|
|
59
|
+
* normalise a base of `/` into a relative result — the one input where the two spellings differ,
|
|
60
|
+
* and the shell half's answer is the absolute one.
|
|
61
|
+
*/
|
|
62
|
+
function resolveMachineDir(variable, fallback) {
|
|
63
|
+
const configured = process.env[variable];
|
|
64
|
+
const base = configured === undefined || configured === '' ? `${homeRoot()}/${fallback}` : configured;
|
|
65
|
+
const stripped = base.endsWith('/') ? base.slice(0, -1) : base;
|
|
66
|
+
return `${stripped}/${MACHINE_DIR_NAME}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The machine-local **state** directory — `${XDG_STATE_HOME:-$HOME/.local/state}/autonomous-sdlc-harness`.
|
|
70
|
+
*
|
|
71
|
+
* What lives in it is machine-scoped run state that no single repository owns: the usage lane's two
|
|
72
|
+
* artifacts, published by `docs/watcher.md` §5, and the registry of initialized repositories, which
|
|
73
|
+
* that section is explicit is a different artifact defined elsewhere.
|
|
74
|
+
*/
|
|
75
|
+
export function machineStateDir() {
|
|
76
|
+
return resolveMachineDir('XDG_STATE_HOME', '.local/state');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The machine-local **configuration** directory — `${XDG_CONFIG_HOME:-$HOME/.config}/autonomous-sdlc-harness`.
|
|
80
|
+
*
|
|
81
|
+
* What lives in it is an operator's own settings rather than a repository's: `docs/watcher.md` §6's
|
|
82
|
+
* `push.env` is the one file this release puts there, and it comes first in that section's
|
|
83
|
+
* precedence because a push credential belongs to a person and a machine rather than to a checkout.
|
|
84
|
+
*/
|
|
85
|
+
export function machineConfigDir() {
|
|
86
|
+
return resolveMachineDir('XDG_CONFIG_HOME', '.config');
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The environment variable that relocates the agent runner's configuration home, and the directory
|
|
90
|
+
* name it falls back to under {@link homeRoot}.
|
|
91
|
+
*/
|
|
92
|
+
const CLAUDE_HOME_VARIABLE = 'CLAUDE_CONFIG_DIR';
|
|
93
|
+
const CLAUDE_HOME_FALLBACK = '.claude';
|
|
94
|
+
/**
|
|
95
|
+
* The **agent runner's** configuration home — `${CLAUDE_CONFIG_DIR:-$HOME/.claude}`.
|
|
96
|
+
*
|
|
97
|
+
* Not this CLI's directory and never written to: what it holds that the CLI reads is the record of
|
|
98
|
+
* which plugins are installed and where (`machine/plugins.ts`), which is the one machine-local fact
|
|
99
|
+
* `init` cannot generate an answer for. It is resolved here rather than beside that reader for the
|
|
100
|
+
* module rule above — one home for the variable, so a scratch home an operator or a test points the
|
|
101
|
+
* runner at moves every reader with it instead of half of them.
|
|
102
|
+
*
|
|
103
|
+
* Same three properties as the two directories above: the variable when it holds something, the
|
|
104
|
+
* fallback otherwise, one trailing slash stripped, and no filesystem touch.
|
|
105
|
+
*/
|
|
106
|
+
export function claudeHome() {
|
|
107
|
+
const configured = process.env[CLAUDE_HOME_VARIABLE];
|
|
108
|
+
const base = configured === undefined || configured === '' ? `${homeRoot()}/${CLAUDE_HOME_FALLBACK}` : configured;
|
|
109
|
+
return base.endsWith('/') ? base.slice(0, -1) : base;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/machine/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAEtC;;;;;;;;GAQG;AACH,SAAS,iBAAiB,CAAC,QAAgB,EAAE,QAAgB;IAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IACtG,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/D,OAAO,GAAG,QAAQ,IAAI,gBAAgB,EAAE,CAAC;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,iBAAiB,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,iBAAiB,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;AACzD,CAAC;AAED;;;GAGG;AACH,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AACjD,MAAM,oBAAoB,GAAG,SAAS,CAAC;AAEvC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACrD,MAAM,IAAI,GACR,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,IAAI,oBAAoB,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IACvG,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where this harness's plugin lives on **this** machine — both roots of it — read from the records
|
|
3
|
+
* the agent runner keeps.
|
|
4
|
+
*
|
|
5
|
+
* **The rule this module exists to enforce: the plugin's roots are resolved here and nowhere else in
|
|
6
|
+
* the CLI, and each is re-derived on every call.** The install root carries the plugin's version in
|
|
7
|
+
* its path, so a value written down once — into a generated permission entry, into a document —
|
|
8
|
+
* stops being true at the next plugin upgrade while still looking right. A caller that wants a root
|
|
9
|
+
* asks for it now; nothing here caches, and nothing here may be persisted.
|
|
10
|
+
*
|
|
11
|
+
* **Why there are two.** A helper named in an **instruction file** arrives as bytes and the agent
|
|
12
|
+
* resolves the root itself; a helper named in an **agent definition body** has `${CLAUDE_PLUGIN_ROOT}`
|
|
13
|
+
* substituted by the runtime. On a `directory`-sourced marketplace those two were measured to differ
|
|
14
|
+
* (2026-08-26): the runtime substituted `<installLocation>/plugin` — the live source tree — while
|
|
15
|
+
* `installPath` named a version-pinned cache snapshot. So {@link pluginInstallRoot} answers "where
|
|
16
|
+
* the runner installed it" and {@link pluginRuntimeRoot} answers "what the runtime substitutes",
|
|
17
|
+
* and on a git-sourced marketplace the two collapse to one directory.
|
|
18
|
+
*
|
|
19
|
+
* The files are the **agent runner's**, not this CLI's, both under {@link claudeHome}'s `plugins/`:
|
|
20
|
+
* `installed_plugins.json` for the install root, and `known_marketplaces.json` — plus the
|
|
21
|
+
* `.claude-plugin/marketplace.json` its `installLocation` points at — for the runtime root. Two
|
|
22
|
+
* consequences follow and both are deliberate.
|
|
23
|
+
*
|
|
24
|
+
* 1. **They are read-only, always.** No function here writes, creates a directory, or repairs
|
|
25
|
+
* anything. A format this CLI does not recognise is the runner's to change and not ours to
|
|
26
|
+
* correct.
|
|
27
|
+
* 2. **Their shape is navigated, not validated.** `version` is read past rather than pinned: a bump
|
|
28
|
+
* that adds a field would otherwise turn every working machine into a finding. Only the fields a
|
|
29
|
+
* root is navigated for are looked at, and they are checked where they are used: `installPath`
|
|
30
|
+
* on a plugin row, and `source.source` plus `installLocation` on a marketplace entry together
|
|
31
|
+
* with the plugin `source` in the manifest that entry locates. That is the opposite of
|
|
32
|
+
* `machine/registry.ts`'s `schema` handling, and the reason is ownership: that file is this
|
|
33
|
+
* CLI's own and a version it does not know means a newer CLI wrote it, while these belong to
|
|
34
|
+
* another program that may change them at any release.
|
|
35
|
+
*
|
|
36
|
+
* **Every failure is `undefined`, never a throw** — file absent, unreadable, unparseable, key
|
|
37
|
+
* absent, no usable row. A machine where the plugin is not installed is an answer a caller has
|
|
38
|
+
* something correct to do with, which is the discipline `core/git.ts`'s `hasCommits` states for the
|
|
39
|
+
* same reason: the one consumer is a `doctor` check, and "not installed" is a state it reports. It
|
|
40
|
+
* is also why {@link pluginRuntimeRoot} falls back to the install root rather than throwing on a
|
|
41
|
+
* record it cannot navigate.
|
|
42
|
+
*/
|
|
43
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
44
|
+
import { join } from 'node:path';
|
|
45
|
+
import { isJsonObject, readJsonFile } from '../core/json.js';
|
|
46
|
+
import { MARKETPLACE_NAME, PLUGIN_KEY, PLUGIN_NAME } from '../generators/projectSettings.js';
|
|
47
|
+
import { claudeHome } from './paths.js';
|
|
48
|
+
/** The runner's plugin directory under {@link claudeHome}, and the two records inside it. */
|
|
49
|
+
const PLUGINS_DIRNAME = 'plugins';
|
|
50
|
+
const INSTALLED_PLUGINS_FILENAME = 'installed_plugins.json';
|
|
51
|
+
const KNOWN_MARKETPLACES_FILENAME = 'known_marketplaces.json';
|
|
52
|
+
/** The object keying every installed plugin's rows, and the fields one row is selected on. */
|
|
53
|
+
const PLUGINS_KEY = 'plugins';
|
|
54
|
+
const INSTALL_PATH_FIELD = 'installPath';
|
|
55
|
+
const SCOPE_FIELD = 'scope';
|
|
56
|
+
const PROJECT_PATH_FIELD = 'projectPath';
|
|
57
|
+
/** The marketplace entry's own fields, and the source type whose install location is a live tree. */
|
|
58
|
+
const SOURCE_FIELD = 'source';
|
|
59
|
+
const DIRECTORY_SOURCE = 'directory';
|
|
60
|
+
const INSTALL_LOCATION_FIELD = 'installLocation';
|
|
61
|
+
/** The marketplace manifest inside an install location, and the field one plugin entry is found by. */
|
|
62
|
+
const MARKETPLACE_MANIFEST_DIRNAME = '.claude-plugin';
|
|
63
|
+
const MARKETPLACE_MANIFEST_FILENAME = 'marketplace.json';
|
|
64
|
+
const NAME_FIELD = 'name';
|
|
65
|
+
/**
|
|
66
|
+
* The directory of helper scripts inside an install root — the segment {@link pluginScriptsDir}
|
|
67
|
+
* joins, and the one a reader outside this module recognises a helper path by. Exported so that
|
|
68
|
+
* reader imports it instead of keeping a second copy: a copy of a literal is a copy that can drift,
|
|
69
|
+
* and reading the name resolves no plugin and touches no filesystem.
|
|
70
|
+
*/
|
|
71
|
+
export const SCRIPTS_DIRNAME = 'scripts';
|
|
72
|
+
/** The suffix a helper script is named with. */
|
|
73
|
+
const SCRIPT_SUFFIX = '.sh';
|
|
74
|
+
/** The record's absolute path. A pure string: asking where it is never creates anything. */
|
|
75
|
+
export function installedPluginsPath() {
|
|
76
|
+
return join(claudeHome(), PLUGINS_DIRNAME, INSTALLED_PLUGINS_FILENAME);
|
|
77
|
+
}
|
|
78
|
+
/** The marketplace register's absolute path, resolved the same pure way. */
|
|
79
|
+
export function knownMarketplacesPath() {
|
|
80
|
+
return join(claudeHome(), PLUGINS_DIRNAME, KNOWN_MARKETPLACES_FILENAME);
|
|
81
|
+
}
|
|
82
|
+
/** One row's install root, or `undefined` when the row is not one or does not carry a usable path. */
|
|
83
|
+
function installPathOf(row) {
|
|
84
|
+
if (!isJsonObject(row))
|
|
85
|
+
return undefined;
|
|
86
|
+
const value = row[INSTALL_PATH_FIELD];
|
|
87
|
+
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
88
|
+
}
|
|
89
|
+
/** Whether a row was installed at the given scope. */
|
|
90
|
+
function atScope(row, scope) {
|
|
91
|
+
return isJsonObject(row) && row[SCOPE_FIELD] === scope;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The install root of this harness's plugin, or `undefined` when this machine has none.
|
|
95
|
+
*
|
|
96
|
+
* **The selection rule, in order**, because one plugin carries a row per scope it was enabled at and
|
|
97
|
+
* they can name different versions:
|
|
98
|
+
*
|
|
99
|
+
* 1. a `project`-scope row whose `projectPath` is `repoRoot` — the row describing *this* repository,
|
|
100
|
+
* which is the one a run started here resolves the plugin through;
|
|
101
|
+
* 2. failing that, a `user`-scope row — enabled once for the account, so it applies here too;
|
|
102
|
+
* 3. failing that, the first row carrying a usable `installPath`, so a scope this CLI has not been
|
|
103
|
+
* taught yet still yields an answer rather than a false "not installed".
|
|
104
|
+
*
|
|
105
|
+
* `repoRoot` is optional so a caller with no repository can still ask; it then starts at step 2.
|
|
106
|
+
*/
|
|
107
|
+
export function pluginInstallRoot(repoRoot) {
|
|
108
|
+
let parsed;
|
|
109
|
+
try {
|
|
110
|
+
parsed = readJsonFile(installedPluginsPath());
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
if (!isJsonObject(parsed))
|
|
116
|
+
return undefined;
|
|
117
|
+
const plugins = parsed[PLUGINS_KEY];
|
|
118
|
+
if (!isJsonObject(plugins))
|
|
119
|
+
return undefined;
|
|
120
|
+
const rows = plugins[PLUGIN_KEY];
|
|
121
|
+
if (!Array.isArray(rows))
|
|
122
|
+
return undefined;
|
|
123
|
+
const usable = rows.filter((row) => installPathOf(row) !== undefined);
|
|
124
|
+
const here = repoRoot === undefined
|
|
125
|
+
? undefined
|
|
126
|
+
: usable.find((row) => atScope(row, 'project') && isJsonObject(row) && row[PROJECT_PATH_FIELD] === repoRoot);
|
|
127
|
+
return installPathOf(here ?? usable.find((row) => atScope(row, 'user')) ?? usable[0]);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The root the **runtime** substitutes for `${CLAUDE_PLUGIN_ROOT}`, or the install root when this
|
|
131
|
+
* machine gives no reason to think they differ.
|
|
132
|
+
*
|
|
133
|
+
* **The one case where they differ is a `directory`-sourced marketplace.** The runner reads such a
|
|
134
|
+
* marketplace's manifest in place — `known_marketplaces.json` gives the entry an `installLocation`
|
|
135
|
+
* equal to the source tree — while still installing the plugin as a version-pinned snapshot under
|
|
136
|
+
* `installPath`. Measured 2026-08-26 on `claude` 2.1.246: a sub-agent's `${CLAUDE_PLUGIN_ROOT}`
|
|
137
|
+
* resolved to `<installLocation>/<the manifest's plugin source>`, not to `installPath`. So the
|
|
138
|
+
* derivation is the runner's own two records, in order: the marketplace entry for the location, and
|
|
139
|
+
* the manifest at that location for this plugin's relative `source`.
|
|
140
|
+
*
|
|
141
|
+
* **Anything else collapses to {@link pluginInstallRoot}** — a git source, an entry this CLI cannot
|
|
142
|
+
* navigate, a manifest that does not name this plugin, a `source` that is not a relative path
|
|
143
|
+
* string. A git-sourced adopter therefore gets one root, which is correct: there the two are the
|
|
144
|
+
* same directory. `repoRoot` is passed through to the fallback and is otherwise unused.
|
|
145
|
+
*/
|
|
146
|
+
export function pluginRuntimeRoot(repoRoot) {
|
|
147
|
+
const fallback = () => pluginInstallRoot(repoRoot);
|
|
148
|
+
let parsed;
|
|
149
|
+
try {
|
|
150
|
+
parsed = readJsonFile(knownMarketplacesPath());
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return fallback();
|
|
154
|
+
}
|
|
155
|
+
if (!isJsonObject(parsed))
|
|
156
|
+
return fallback();
|
|
157
|
+
const entry = parsed[MARKETPLACE_NAME];
|
|
158
|
+
if (!isJsonObject(entry))
|
|
159
|
+
return fallback();
|
|
160
|
+
const source = entry[SOURCE_FIELD];
|
|
161
|
+
if (!isJsonObject(source) || source[SOURCE_FIELD] !== DIRECTORY_SOURCE)
|
|
162
|
+
return fallback();
|
|
163
|
+
const location = entry[INSTALL_LOCATION_FIELD];
|
|
164
|
+
if (typeof location !== 'string' || location === '')
|
|
165
|
+
return fallback();
|
|
166
|
+
let manifest;
|
|
167
|
+
try {
|
|
168
|
+
manifest = readJsonFile(join(location, MARKETPLACE_MANIFEST_DIRNAME, MARKETPLACE_MANIFEST_FILENAME));
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return fallback();
|
|
172
|
+
}
|
|
173
|
+
if (!isJsonObject(manifest))
|
|
174
|
+
return fallback();
|
|
175
|
+
const plugins = manifest[PLUGINS_KEY];
|
|
176
|
+
if (!Array.isArray(plugins))
|
|
177
|
+
return fallback();
|
|
178
|
+
const row = plugins.find((candidate) => isJsonObject(candidate) && candidate[NAME_FIELD] === PLUGIN_NAME);
|
|
179
|
+
const relative = isJsonObject(row) ? row[SOURCE_FIELD] : undefined;
|
|
180
|
+
if (typeof relative !== 'string' || relative === '')
|
|
181
|
+
return fallback();
|
|
182
|
+
return join(location, relative);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The helper scripts shipped inside an install root, by filename, in codepoint order.
|
|
186
|
+
*
|
|
187
|
+
* **Read from the directory rather than listed here**, and that is the point: the names are declared
|
|
188
|
+
* once, in the plugin's own `scripts/README.md`, and a copy in the CLI would be a second declaration
|
|
189
|
+
* that drifts the first time a helper is added or renamed. Sorted so one machine reports them the
|
|
190
|
+
* same way twice, whatever order the filesystem enumerates in.
|
|
191
|
+
*
|
|
192
|
+
* An unreadable or absent directory is an empty list, for the module header's reason: it is the
|
|
193
|
+
* runner's tree, and a caller grades what it found rather than being stopped by what it did not.
|
|
194
|
+
*/
|
|
195
|
+
export function pluginHelperScripts(installRoot) {
|
|
196
|
+
const dir = pluginScriptsDir(installRoot);
|
|
197
|
+
let names;
|
|
198
|
+
try {
|
|
199
|
+
names = readdirSync(dir);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
return names
|
|
205
|
+
.filter((name) => name.endsWith(SCRIPT_SUFFIX))
|
|
206
|
+
.filter((name) => {
|
|
207
|
+
try {
|
|
208
|
+
return statSync(join(dir, name)).isFile();
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
.sort();
|
|
215
|
+
}
|
|
216
|
+
/** `<installRoot>/scripts` — the directory {@link pluginHelperScripts} enumerates. */
|
|
217
|
+
export function pluginScriptsDir(installRoot) {
|
|
218
|
+
return join(installRoot, SCRIPTS_DIRNAME);
|
|
219
|
+
}
|
|
220
|
+
/** `<installRoot>/scripts/<name>` — the path a helper is invoked by, joined here and nowhere else. */
|
|
221
|
+
export function pluginHelperPath(installRoot, name) {
|
|
222
|
+
return join(pluginScriptsDir(installRoot), name);
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=plugins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["../../src/machine/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAkB,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC7F,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,6FAA6F;AAC7F,MAAM,eAAe,GAAG,SAAS,CAAC;AAClC,MAAM,0BAA0B,GAAG,wBAAwB,CAAC;AAC5D,MAAM,2BAA2B,GAAG,yBAAyB,CAAC;AAE9D,8FAA8F;AAC9F,MAAM,WAAW,GAAG,SAAS,CAAC;AAC9B,MAAM,kBAAkB,GAAG,aAAa,CAAC;AACzC,MAAM,WAAW,GAAG,OAAO,CAAC;AAC5B,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAEzC,qGAAqG;AACrG,MAAM,YAAY,GAAG,QAAQ,CAAC;AAC9B,MAAM,gBAAgB,GAAG,WAAW,CAAC;AACrC,MAAM,sBAAsB,GAAG,iBAAiB,CAAC;AAEjD,uGAAuG;AACvG,MAAM,4BAA4B,GAAG,gBAAgB,CAAC;AACtD,MAAM,6BAA6B,GAAG,kBAAkB,CAAC;AACzD,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC;AAEzC,gDAAgD;AAChD,MAAM,aAAa,GAAG,KAAK,CAAC;AAE5B,4FAA4F;AAC5F,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,eAAe,EAAE,0BAA0B,CAAC,CAAC;AACzE,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,qBAAqB;IACnC,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,eAAe,EAAE,2BAA2B,CAAC,CAAC;AAC1E,CAAC;AAED,sGAAsG;AACtG,SAAS,aAAa,CAAC,GAA0B;IAC/C,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,KAAK,GAAG,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACtC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC;AAED,sDAAsD;AACtD,SAAS,OAAO,CAAC,GAAc,EAAE,KAAa;IAC5C,OAAO,YAAY,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,KAAK,KAAK,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAiB;IACjD,IAAI,MAA6B,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,YAAY,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAE5C,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IACpC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAE7C,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAE3C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC;IACtE,MAAM,IAAI,GACR,QAAQ,KAAK,SAAS;QACpB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,kBAAkB,CAAC,KAAK,QAAQ,CAAC,CAAC;IACjH,OAAO,aAAa,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACxF,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAiB;IACjD,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAEnD,IAAI,MAA6B,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,YAAY,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QAAE,OAAO,QAAQ,EAAE,CAAC;IAE7C,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACvC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,QAAQ,EAAE,CAAC;IAE5C,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;IACnC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,gBAAgB;QAAE,OAAO,QAAQ,EAAE,CAAC;IAE1F,MAAM,QAAQ,GAAG,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC/C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,QAAQ,EAAE,CAAC;IAEvE,IAAI,QAA+B,CAAC;IACpC,IAAI,CAAC;QACH,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,4BAA4B,EAAE,6BAA6B,CAAC,CAAC,CAAC;IACvG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,EAAE,CAAC;IAE/C,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,QAAQ,EAAE,CAAC;IAE/C,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,WAAW,CAAC,CAAC;IAC1G,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnE,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,QAAQ,EAAE,CAAC;IAEvE,OAAO,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,MAAM,GAAG,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK;SACT,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;SAC9C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QACf,IAAI,CAAC;YACH,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC;SACD,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;AAC5C,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,IAAY;IAChE,OAAO,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,CAAC;AACnD,CAAC"}
|