aimship 0.0.0-stage → 0.1.0-preview.4

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 (33) hide show
  1. package/CONTRIBUTING.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +44 -2
  4. package/README.zh-CN.md +45 -0
  5. package/bin/aimship.mjs +17 -0
  6. package/docs/cli.md +62 -0
  7. package/docs/guide.md +59 -0
  8. package/docs/installation.md +117 -0
  9. package/lib/cli.mjs +159 -0
  10. package/lib/install.mjs +363 -0
  11. package/lib/io.mjs +55 -0
  12. package/lib/state.mjs +210 -0
  13. package/package.json +42 -4
  14. package/skills/aimship/SKILL.md +19 -0
  15. package/skills/aimship/agents/openai.yaml +6 -0
  16. package/skills/aimship/references/core.md +84 -0
  17. package/skills/aimship/references/document-patterns.md +19 -0
  18. package/skills/aimship/references/hosts.md +15 -0
  19. package/skills/aimship/references/recovery.md +13 -0
  20. package/skills/aimship/references/sources-and-changes.md +28 -0
  21. package/skills/aimship/references/stages/contract.md +19 -0
  22. package/skills/aimship/references/stages/diagnose.md +19 -0
  23. package/skills/aimship/references/stages/frame.md +19 -0
  24. package/skills/aimship/references/stages/handoff.md +19 -0
  25. package/skills/aimship/references/stages/implement.md +19 -0
  26. package/skills/aimship/references/stages/plan.md +19 -0
  27. package/skills/aimship/references/stages/prototype.md +19 -0
  28. package/skills/aimship/references/stages/requirements.md +19 -0
  29. package/skills/aimship/references/stages/review.md +19 -0
  30. package/skills/aimship/references/stages/scope.md +19 -0
  31. package/skills/aimship/references/stages/tech-design.md +19 -0
  32. package/skills/aimship/references/stages/test-design.md +19 -0
  33. package/skills/aimship/references/stages/verify.md +19 -0
@@ -0,0 +1,11 @@
1
+ # Contributing
2
+
3
+ This repository is standalone. Runtime, tests and packaging must not read a parent workspace or private planning material. Use synthetic fixtures and temporary user homes for installation tests; never test by overwriting real skills, configurations, delivery materials or business projects.
4
+
5
+ Node.js 22+ is the intended runtime floor. Run `npm test` for deterministic tool tests (one worker by default) and `npm run check:package` for distributable-content checks. `npm pack` creates a local package. There are no runtime third-party dependencies.
6
+
7
+ Keep agent workflow semantics in `skills/aimship/references`, deterministic file/state/install behavior in `lib`, and host entry handling thin. Changes should be driven by requirements or observed failures, with appropriate checks. Do not introduce route taxonomies, automatic agent scheduling, per-batch full testing gates, or a generic workflow platform.
8
+
9
+ A passing structural test does not prove agent behavior. Record actual host/version/model/permission conditions for behavioral checks; leave unavailable combinations unverified. Review affected changes and preserve historical failure evidence when retesting.
10
+
11
+ Use GitHub Issues for bugs and feature suggestions. Provide the version, host details and a minimal synthetic reproduction; redact credentials, personal paths and delivery data.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 clsx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,45 @@
1
- # Temporary Holding Version
1
+ # Aimship
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [中文](README.zh-CN.md)
4
+
5
+ Aimship helps you organize and carry out development tasks in AI coding tools. Use it to develop features, fix bugs, refactor code or prepare a design.
6
+
7
+ From the initial request to delivery, Aimship arranges the stages needed for the task and keeps a record of requirements, designs, progress and results. When requirements change, the relevant materials are updated. New conversations can continue from those records, reducing the need to explain the background again or organize progress notes manually.
8
+
9
+ Each task has its own delivery folder under `~/Aimship/`, while code stays in its repository. You can work on several independent tasks at once, or limit a task to requirements analysis or design.
10
+
11
+ ## Installation
12
+
13
+ Requires Node.js 22+ and npm. Select a host with `--host`: `codex`, `cursor` or `claude-code`.
14
+
15
+ ```sh
16
+ npx aimship@preview install --host codex
17
+ ```
18
+
19
+ The npm command becomes available when the preview is published. For the current source or a local package, see the [installation guide](docs/installation.md).
20
+
21
+ Aimship shares one installation across projects and selected hosts. It does not require a global npm install. After installation, start a new host session and enable access to the delivery folder and code repositories as needed.
22
+
23
+ ## Usage
24
+
25
+ Select Aimship in the host: `$aimship` in Codex CLI, or `/aimship` in Cursor and Claude Code. Describe the intended result:
26
+
27
+ > Use Aimship to add Excel export to the order list.
28
+
29
+ Aimship uses the request and available materials to clarify the scope and arrange the work. Include any specific limits in your request, such as “Design only for this task.”
30
+
31
+ To continue in a new session, provide the existing delivery directory:
32
+
33
+ > Use Aimship to continue the task in `~/Aimship/order-export/`.
34
+
35
+ Use the same delivery directory when switching hosts, with access authorized separately in each host. Multiple independent deliveries can progress at once; keep one writing session per delivery. See the [user guide](docs/guide.md#work-on-two-requests-at-once) for shared-repository and worktree examples.
36
+
37
+ ## Documentation and feedback
38
+
39
+ - [Installation guide](docs/installation.md): host setup, updates, version and status queries, uninstall and troubleshooting.
40
+ - [User guide](docs/guide.md): scope, stages, changes, parallel deliveries and session handoff.
41
+ - [Tool protocol](docs/cli.md): state and recovery commands for agents and contributors.
42
+
43
+ The preview is intended for experimentation and feedback. Host integration requirements are described in the installation guide. Report bugs and feature requests through [GitHub Issues](https://github.com/clsx/aimship/issues). See [Contributing](CONTRIBUTING.md) to contribute.
44
+
45
+ Released under the [MIT License](LICENSE).
@@ -0,0 +1,45 @@
1
+ # Aimship
2
+
3
+ [English](README.md)
4
+
5
+ Aimship 帮助你在 AI 编程工具中组织和推进开发任务,适用于功能开发、问题修复、技术重构和方案设计。
6
+
7
+ 从提出需求到完成交付,Aimship 根据任务需要安排工作阶段,并保存需求、设计、进度和结果。需求发生变化时,相关资料随之更新;切换会话后,可以根据已有记录继续工作,减少重复解释背景和手动整理进度的负担。
8
+
9
+ 每项任务在 `~/Aimship/` 下拥有独立的交付资料目录,代码保留在原仓库。你可以同时推进多个独立任务,也可以只委托其中的需求分析或设计工作。
10
+
11
+ ## 安装
12
+
13
+ 需要 Node.js 22+ 和 npm。通过 `--host` 选择宿主:`codex`、`cursor` 或 `claude-code`。
14
+
15
+ ```sh
16
+ npx aimship@preview install --host codex
17
+ ```
18
+
19
+ 此 npm 命令在预览版发布后可用。当前源码或本地安装包的使用方式见[安装指南](docs/installation.md)。
20
+
21
+ 多个项目和已接入宿主共用一份 Aimship,无需全局安装 npm 包。安装后开启新的宿主会话,并按实际需要授权访问交付目录和代码仓库。
22
+
23
+ ## 使用
24
+
25
+ 在宿主中选择 Aimship:Codex CLI 使用 `$aimship`,Cursor 和 Claude Code 使用 `/aimship`,随后描述预期结果:
26
+
27
+ > 使用 Aimship,为订单列表增加导出 Excel 的功能。
28
+
29
+ Aimship 会根据需求和已有资料帮助明确范围、安排工作。有具体限制时,可以在需求中补充,例如“本次只完成设计”。
30
+
31
+ 在新会话中继续工作时,提供已有交付目录:
32
+
33
+ > 使用 Aimship 继续 `~/Aimship/order-export/` 中的任务。
34
+
35
+ 切换宿主时使用同一交付目录,并分别完成访问授权。多个独立交付可以同时推进;同一次交付保持一个写入会话。同仓库与 worktree 的使用实例见[使用指南](docs/guide.md#work-on-two-requests-at-once)。
36
+
37
+ ## 文档与反馈
38
+
39
+ - [安装指南](docs/installation.md):宿主接入、更新、版本与状态查询、卸载及常见问题。
40
+ - [使用指南](docs/guide.md):范围、阶段、变更、并行交付与会话交接。
41
+ - [工具协议](docs/cli.md):供 Agent 与贡献者使用的状态及恢复命令。
42
+
43
+ 预览版用于试用和收集反馈,宿主集成要求见安装指南。Bug 和功能建议请通过[GitHub Issues](https://github.com/clsx/aimship/issues) 提交;参与贡献请参阅[贡献说明](CONTRIBUTING.md)。
44
+
45
+ 本项目采用 [MIT 许可证](LICENSE)。
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from 'node:url';
3
+ import path from 'node:path';
4
+ import fs from 'node:fs/promises';
5
+ import { run } from '../lib/cli.mjs';
6
+
7
+ export async function main(argv = process.argv.slice(2), options = {}) {
8
+ try {
9
+ const data = await run(argv, { sourceRoot: path.dirname(path.dirname(fileURLToPath(import.meta.url))), ...options });
10
+ process.stdout.write(JSON.stringify({ ok: true, ...data }, null, 2) + '\n');
11
+ } catch (error) {
12
+ process.stderr.write(JSON.stringify({ ok: false, error: { code: error.code ?? 'IO_ERROR', message: error.message, recovery: error.recovery ?? 'Inspect the error and existing files; retry only after resolving the cause.', ...(error.details === undefined ? {} : { details: error.details }) } }, null, 2) + '\n');
13
+ process.exitCode = 1;
14
+ }
15
+ }
16
+ const invokedPath = process.argv[1] ? await fs.realpath(process.argv[1]).catch(() => null) : null;
17
+ if (invokedPath === await fs.realpath(fileURLToPath(import.meta.url))) await main();
package/docs/cli.md ADDED
@@ -0,0 +1,62 @@
1
+ # Local tool protocol
2
+
3
+ The agent normally runs these tools to handle file structure and version/revision checks, not business decisions. Cross-delivery overlap checking is an agent workflow using existing state and focused local Git metadata, not a CLI concurrency guarantee or a new registry. All commands emit JSON; errors include a code, message and recovery advice with a nonzero exit status. Run `node bin/aimship.mjs help` in a source checkout for the complete current interface.
4
+
5
+ ## Bind and create
6
+
7
+ An installed entry invokes the stable `.aimship/bin/aimship.mjs` launcher. `bind` returns the active immutable release, core, stage directory, CLI path, rules version, configured material root and config/custom locations. Use the returned fixed CLI and version for this session. Paths in examples below are placeholders to replace with your actual paths, safely quoted for your shell.
8
+
9
+ ```sh
10
+ node '<stable-launcher>' bind
11
+ node '<fixed-cli>' init '<delivery-dir>' --rules-version '0.1.0-preview.4' --input '<init.json>'
12
+ ```
13
+
14
+ Before `init`, save the original request and current requirements/Scope in the actual delivery directory. `init` creates state once; it does not create requirement templates or infer authority. Minimal input:
15
+
16
+ ```json
17
+ {
18
+ "goal": {"summary": "Explain the selected design", "endpoint": "Design handoff"},
19
+ "goalRef": "requirements.md",
20
+ "stageRuns": [],
21
+ "next": {"action": "Record the agreed stage arrangement", "inputRefs": ["requirements.md"]}
22
+ }
23
+ ```
24
+
25
+ Identity, schema/rules version, revision and timestamps are tool-managed. Do not include them in init input. A useful arrangement contains distinct `stageRuns` with `id`, `stage`, `scope`, `progress`, `inputRefs`, and `outputRefs`. `currentRun` names one of them or is null. Associate repositories as `{id, path, role}` with an absolute actual checkout/worktree root. IDs are local to this delivery; they do not establish cross-delivery repository identity. Use role and current scope to distinguish read-only reference from intended code writes; record actual authorized scope and working basis in the associated requirements/evidence.
26
+
27
+ ## Read, update and check
28
+
29
+ ```sh
30
+ node '<fixed-cli>' read '<delivery-dir>' --rules-version '0.1.0-preview.4'
31
+ node '<fixed-cli>' update '<delivery-dir>' --revision 0 --rules-version '0.1.0-preview.4' --input '<patch.json>'
32
+ node '<fixed-cli>' check '<delivery-dir>' --rules-version '0.1.0-preview.4'
33
+ node '<fixed-cli>' resume '<delivery-dir>' --rules-version '0.1.0-preview.4'
34
+ ```
35
+
36
+ Use the revision just read; a conflict requires rereading and reconciling. Updates recursively merge objects but replace arrays in full. `null` is a value, not deletion. `--input -` reads a JSON object from stdin. Completed stage runs need `resultRef`; retained old results use `priorResultRef` or `historyResultRefs`. Removing an already-started run or historical result reference is rejected.
37
+
38
+ References are delivery-relative, `repo:<id>/<relative-path>`, or HTTP(S) URLs, with optional fragments. Current checks inspect goal/next/current-input paths; `--all` also checks registered historical, decision, outcome, checkpoint and rule-change references. A file-existence result does not validate its fragment, contents or meaning. Remote references are not fetched by this tool. `resume.canProceed` is false for a recorded blocked current run, missing input paths or version mismatch. A true value only means these structural blockers were not found; the agent still checks authority, unresolved decisions and stage completion. Unknown schemas remain readable but cannot be overwritten.
39
+
40
+ `progress` is planned/active/blocked/done. Results are passed/failed/not-run/unknown/mixed; applicability is current/needs-review/unknown. `outcome.goal` is in-progress/met/not-met/unknown; acceptance is not-recorded/accepted/declined/unknown. A met goal requires a goal conclusion reference; acceptance requires its own decision reference. These are separate from merge/deployment references. The agent must assess the evidence; fields are not proof of success.
41
+
42
+ Changing the goal or goal reference requires a new `goal.decisionRef`; the tool preserves the old goal and outcome. Reopening increments `cycle` by one, retains a prior closure reference and resets current acceptance. Important changes remain in `openChanges` with stable ID/reference and open/closed status rather than disappearing when resolved.
43
+
44
+ ## Recovery and rules adoption
45
+
46
+ Writes save `state.previous.json` and replace state atomically. If state is damaged, inspect actual artifacts and the backup before explicit recovery:
47
+
48
+ ```sh
49
+ node '<fixed-cli>' recover '<delivery-dir>' --revision '<backup-revision>' --rules-version '<bound-version>'
50
+ ```
51
+
52
+ Recovery preserves damaged bytes and refuses to roll back a valid or newer unknown state. It cannot reconstruct unrecorded effects. A stale lock requires confirming there is no active writer before removing only that lock. No parallel workflow scheduling is provided.
53
+
54
+ When an upgrade makes an old session mismatch, stop new stages. A compatible old tool can `checkpoint` only `{checkpoint: {summary, ref?}, next: {...}}` with its old rules version and current revision. It cannot complete a run or goal. Reload the active rules, inspect compatibility at a stable point, then use the active tool's `adopt` with input `{compatibilityRef: "results.md#rules-switch"}`. This records a rule switch without changing historical business facts. Schema 1 is the initial format; no legacy DeliveryArc migration or invented older Aimship migration is provided. Unsupported formats require a future supported migration and remain preserved meanwhile.
55
+
56
+ ## Installation diagnostics
57
+
58
+ User-facing installation, explicit update, version/status and uninstall commands are documented in one [installation guide](installation.md). `install` performs software installation and updates; this protocol's `update` continues to mean a revision-checked delivery-state update.
59
+
60
+ `--version` distinguishes the CLI/tool version from the active installed release. Use the stable launcher for the current installation. `status` is read-only, inspects the known installation and managed entry files, and reports registration separately from entry integrity. It does not establish host loading, login, permissions or semantic delivery readiness. The key fields are `installation`, `healthy`, `installedActiveVersion`, `pending`, `registeredHosts`, `hosts`, and `issues`. Each host separates `registered`, `entryPresent`, `entryIntegrity`, `sharedWith`, `hostLoaded`, `login`, and `permissions`; the final three remain `not-verified`. `unselectedHostDiscoveryPossible` warns about compatible discovery. A diagnostic command may exit successfully while reporting `healthy: false`; inspect its data. `--version` includes `toolVersion` and `installedActiveVersion`, which may differ when called from an older release.
61
+
62
+ Installation transition results include previous and target versions; failures retain pending information for recovery. Disconnecting a named host retains the core, even when no registered hosts remain; full uninstall is a separate invocation without `--host`. Shared entries needed by another registration remain present. No purge-history command exists.
package/docs/guide.md ADDED
@@ -0,0 +1,59 @@
1
+ # Using Aimship
2
+
3
+ ## Describe the work and its endpoint
4
+
5
+ Explicitly select Aimship and say what result you want, where the relevant materials/code are, and how far the agent may proceed. An existing clear instruction is enough; you do not need to prepare a complete PRD. A design-only goal may need no code repository.
6
+
7
+ The agent records the original request and current requirements/Scope, proposes the stages needed for this goal, and explains meaningful choices. Once the arrangement is accepted, it remains the execution order. You decide material business, scope, compatibility, cost or data choices; routine engineering choices do not require repeated approval. Silence does not approve an unanswered proposal.
8
+
9
+ Materials default to a delivery folder beneath `~/Aimship`, configurable at installation. Code remains in place, linked by repository identity and location in the delivery state. The material directory need not be the host's main workspace. Access is handled when an actual operation needs it; the agent must not silently move the materials or disable host controls.
10
+
11
+ ## Work on two requests at once
12
+
13
+ You can keep `~/Aimship/notification-preferences/` and `~/Aimship/invoice-export/` active at the same time. Each has its own goal, stages and state, with one writing session per delivery. They may use different repositories or the same repository.
14
+
15
+ For example, request A is implementing in an app checkout while request B is designing invoice export using that code as a read-only reference. B may continue design after a brief overlap note. Before B writes code, the agent checks the recorded work again: if both would edit A's checkout, use your existing choice to implement sequentially or use a separate worktree/checkout. If B already has an isolated worktree, reuse it. Neither file isolation nor finishing B's branch means the changes are already merged or business-compatible. The handoff names the actual code directories, branch/commit, uncommitted work and pending integration.
16
+
17
+ When code scope is associated/changed, implementation begins or a working directory changes, the agent checks accessible delivery states in known material roots, including older unfinished deliveries rather than only recent ones. It reads focused Scope/plan sections only if needed. Ended deliveries are not counted as active writers; their pending integration may still be a dependency. Known shared interfaces, databases or test resources are arranged in the existing plan, even across different repositories.
18
+
19
+ This reports overlaps found in recorded deliveries. Stale records, inaccessible or unknown custom roots and work outside Aimship can hide overlaps. No matching record is not a guarantee of safety. There is no global lock, automatic worktree creation, agent scheduling or automatic merge; the agent preserves existing branches and unknown changes.
20
+
21
+ ## Keep only useful artifacts
22
+
23
+ Every delivery has current requirements and state, with the original request traceable. A small fix can use a short requirement, state, and a results document with separate diagnosis/review/verification/handoff sections. A larger feature can have interaction, technical design, contracts, a batch plan and test cases. Unselected responsibilities do not produce empty templates.
24
+
25
+ External PRDs, prototypes and images remain unchanged. Scope identifies their adopted versions, requirement mappings, conflicts and decisions. A link alone may not preserve a source version. Text extraction, visual inspection, browser interaction and original-format editing are different capabilities; the agent must identify missing parts.
26
+
27
+ Reuse existing engineering HTTP contracts. If database structures or data change, the design links a separate delivery-local SQL file with its dialect and conditions. SQL is a normal deliverable: generating it does not authorize changing migrations or running it.
28
+
29
+ ## Review, verify and change
30
+
31
+ For multiple batches, implement and review each batch, review the integrated change, then execute the independent verification plan. Test code can be developed during implementation. A single review can cover both batch and overall scope for a single batch if the record says so.
32
+
33
+ Results identify the actual code/document version, scope, executor, environment and evidence. Review is not a runtime test. Missing accounts, tools or environments remain unexecuted checks with a concrete next action; they are not deleted to produce a passing report.
34
+
35
+ A meaningful change gets one record explaining reason, old/new meaning, decision and affected work. Editable requirements/design/contracts/SQL/tests are updated, affected conclusions are reviewed, and old evidence remains historical. Unrelated work is not reset. An implementation defect is fixed against the requirement, not hidden by weakening it.
36
+
37
+ ## Continue in another conversation
38
+
39
+ At a stable phase or batch boundary ask for a handoff to a new conversation. The agent saves state, current facts, unresolved changes and next-step references, then supplies a short prompt with the absolute delivery path. Open the new conversation yourself and explicitly resume Aimship there. A different host uses that same directory and state; do not copy the materials. It needs its own authorized access to the material and code roots.
40
+
41
+ The new conversation reads that state and the needed current inputs. It does not need the entire chat, repeat agreed goals, or rerun every test. After compression it reloads missing rules and inputs using the saved binding. If the location is lost, provide the delivery path. Avoid concurrent writing sessions for the same delivery; independent deliveries can progress separately.
42
+
43
+ If a referenced repository moved, give the new location; the agent checks identity before repairing it. Interrupted operations need inspection of actual artifacts before continuation, especially operations with side effects. A revision conflict is reconciled, never force-overwritten.
44
+
45
+ ## Update or stop using Aimship
46
+
47
+ Use the single [installation guide](installation.md) for commands, host reload differences and troubleshooting.
48
+
49
+ Explicitly install the desired local release/version using the same installer. Daily work uses fixed local files, not repeated `npx ...@latest` calls. Installation preserves customizations and delivery content, and reports conflicts with modified or unrelated skill files.
50
+
51
+ When a session's old rules differ from the current installation, finish saving current facts and switch at a stable point. Reload the new core/current stage, assess compatibility and adopt the new rules. If context cannot reliably refresh, resume the same delivery in a new conversation. Unknown formats remain read-only; unsupported migration is reported rather than guessed. Ended deliveries are not swept or rewritten during upgrade.
52
+
53
+ Uninstall through the stable launcher as documented in [the CLI guide](cli.md). It removes only confirmed managed files, retaining delivery materials, custom content and necessary configuration. Removing delivery data is a separate explicit action.
54
+
55
+ ## Decide when work is done
56
+
57
+ The endpoint controls completion. A design commission ends with its agreed design; development includes its required validation. Asking to see current results does not shorten the goal. An explicit decision to end earlier preserves the original goal and outstanding work in the handoff.
58
+
59
+ Goal achieved, user acceptance, merge and deployment remain separate. Small later corrections can reopen the delivery while retaining its previous closure; a new goal or substantial redesign starts a related delivery. Aimship does not automatically manage parent/child work or multiple agents.
@@ -0,0 +1,117 @@
1
+ # Installation and lifecycle
2
+
3
+ ## Installation layout
4
+
5
+ Aimship installs fixed releases under `~/.aimship/` and a stable launcher at `~/.aimship/bin/aimship.mjs`. User configuration and custom content are separate from release files. A current release and at most one ordinary recovery release are retained; changed or unowned files are preserved and reported.
6
+
7
+ Choose `codex`, `cursor` or `claude-code` using repeatable `--host`. Registered hosts use one core and rules version. No per-project install, administrator privilege, npm global install, background updater or resident process is required. Source checkout, local tarball and registry examples all invoke this same installer; choose the appropriate source, do not stack them as separate host installations or add manual skill copies alongside managed entries. Installing software, connecting a host and starting a delivery are separate actions. Installation never creates `~/Aimship` or an empty delivery and never changes code repositories.
8
+
9
+ Node 22+ and npm are required. Installation and tool behavior have been checked on macOS arm64 with Node 24 and 26. Node 22 and other operating systems are not yet verified. Shell examples below use a POSIX shell. Preserve any existing installation before resolving conflicts.
10
+
11
+ ## Source and local packages
12
+
13
+ From the source checkout:
14
+
15
+ ```sh
16
+ node bin/aimship.mjs install --host codex
17
+ ```
18
+
19
+ From the directory containing a supplied tarball:
20
+
21
+ ```sh
22
+ npx --offline file:./aimship-0.1.0-preview.4.tgz install --host codex
23
+ ```
24
+
25
+ Run these against your real home only when you intend to install. Repeat `--host` to connect multiple hosts in that operation. No login to npm is needed to consume a local tarball; npm still needs a writable cache. The installer reports the installation home, previous/target version, action/result, registered hosts and entry locations. A zero exit status is not proof that a host has loaded the skill.
26
+
27
+ ### Isolated trial
28
+
29
+ These commands use one disposable installation and npm cache, not your real configuration. Put the candidate tarball in your current directory first:
30
+
31
+ ```sh
32
+ trial=$(mktemp -d)
33
+ trial=$(cd "$trial" && pwd -P)
34
+ npx --yes --offline --cache "$trial/npm-cache" file:./aimship-0.1.0-preview.4.tgz install --home "$trial/home" --host codex
35
+ node "$trial/home/.aimship/bin/aimship.mjs" --version
36
+ node "$trial/home/.aimship/bin/aimship.mjs" status
37
+ node "$trial/home/.aimship/bin/aimship.mjs" install --host cursor
38
+ node "$trial/home/.aimship/bin/aimship.mjs" uninstall --host cursor
39
+ node "$trial/home/.aimship/bin/aimship.mjs" uninstall
40
+ ```
41
+
42
+ `--home` redirects only Aimship's managed installation. It does not make the running host discover this home, log in, or inherit permissions. The installed launcher and fixed release CLI default later management operations to their own installation home. An explicit `--home` overrides that target. After inspection, the disposable directory can be removed by its owner; do not substitute a real home for this temporary variable.
43
+
44
+ ## Registry installation after publication
45
+
46
+ This candidate has not been published. After the maintainer confirms the package identity and publishes this exact preview, the proposed initial command is:
47
+
48
+ ```sh
49
+ npx aimship@preview install --host codex
50
+ ```
51
+
52
+ The `preview` tag selects the preview channel. To select an exact version, replace `@preview` with its version number. Confirm the package installation when npm prompts. npm obtains the installer; it copies a complete local release, so subsequent use does not depend on that npm cache. Do not use an unversioned command and assume it selects the preview channel. A missing package/version is a distribution failure, not a successful installation.
53
+
54
+ ## Host entry and reload
55
+
56
+ | Host | Managed entry | Explicit use | After install, update or disconnect |
57
+ | --- | --- | --- | --- |
58
+ | Codex | `~/.agents/skills/aimship/` with `agents/openai.yaml` | Select Aimship; CLI `$aimship` | Start a new conversation or restart the CLI, then inspect its actual skill selector. If stale, restart the app. |
59
+ | Cursor | Same `.agents/skills/aimship/` entry | `/aimship` | Start a new agent session; inspect Customize → Skills in the editor. Restart the host if the list remains stale. |
60
+ | Claude Code | `~/.claude/skills/aimship/` | `/aimship` | Start a new session and inspect slash-command discovery; restart the host if stale. |
61
+
62
+ These host integrations are experimental; end-to-end host behavior and cross-host continuation have not yet been verified. Check skill discovery and directory access in your host before starting a delivery. Each host needs its own authentication, model availability and permission to access materials and code. Do not disable permission controls or grant unrelated directories. No cloud synchronization is installed or enabled.
63
+
64
+ Codex and Cursor share the managed entry. Cursor also documents compatibility discovery from Claude/Codex directories. Aimship registers only selected hosts; it cannot guarantee that an unselected compatible host will not display an entry. If two same-name entries appear, inspect their paths and installation version rather than deleting unrelated files. Shared entry presence is distinct from registration, host discovery, login and permission. See the official [Cursor directories](https://cursor.com/docs/skills) and [Codex skill locations](https://learn.chatgpt.com/docs/build-skills).
65
+
66
+ After an update, a running conversation may retain old rules. Save current facts, reload the bound core and current stage at a stable point, and use the existing compatibility/adoption process; start a new conversation if context cannot reliably refresh. Version mismatch stops new workflow writes. Never infer that installation instantly changed an active conversation.
67
+
68
+ ## Add a host, inspect and update
69
+
70
+ Reuse the installed core without fetching another package:
71
+
72
+ ```sh
73
+ node "$HOME/.aimship/bin/aimship.mjs" install --host cursor
74
+ node "$HOME/.aimship/bin/aimship.mjs" --version
75
+ node "$HOME/.aimship/bin/aimship.mjs" status
76
+ ```
77
+
78
+ Repeating the same install is safe for unchanged managed files. Adding a host preserves existing registrations. `status` reads Aimship's known installation files and managed entries; it does not scan projects or query host accounts. It separates registered hosts, missing/modified files, shared entries, pending installation and active version from unverified host capabilities. It is diagnostic, not a semantic delivery validator.
79
+
80
+ To update from a new local release, run that release's `install` command with the same installation home. No `--host` is necessary for an existing installation: registered hosts are retained, and a core whose last host was disconnected remains a core-only installation. After publication, the explicit preview-channel update is:
81
+
82
+ ```sh
83
+ npx aimship@preview install
84
+ ```
85
+
86
+ There is one installation/update operation: `install`. `update` in the low-level CLI modifies delivery state and is **not** a software updater. The installer reports old and target versions and the result, updates the shared core and selected entries, and preserves configuration, custom content and every delivery. Completed deliveries are not traversed or rewritten. No automatic update runs in the background.
87
+
88
+ ## Disconnect or uninstall
89
+
90
+ Disconnect one host, leaving the core and other registrations usable:
91
+
92
+ ```sh
93
+ node "$HOME/.aimship/bin/aimship.mjs" uninstall --host cursor
94
+ ```
95
+
96
+ If another registered host needs a shared entry, that file is retained and reported. Disconnecting the last host retains the core so version/status and later host connection remain available. A compatible host may still discover an entry used by another host; this command does not change that host's settings.
97
+
98
+ Remove all managed programs and entries:
99
+
100
+ ```sh
101
+ node "$HOME/.aimship/bin/aimship.mjs" uninstall
102
+ ```
103
+
104
+ Full uninstall retains all delivery materials, user code, custom files and necessary configuration. Modified/unowned files are preserved and listed. No option purges delivery history. The launcher is removed on complete removal; inspect the returned result. Restart existing host sessions to discard cached rules.
105
+
106
+ ## Troubleshooting and recovery
107
+
108
+ - **Foreign or modified entry/release:** read the reported path, keep its contents and resolve ownership or merge customizations explicitly. Do not force-overwrite it to obtain a successful exit status.
109
+ - **Partial installation:** `status` can identify pending work. Rerun the exact pending package against the same home after correcting the reported cause. Binding/new workflow writes remain suspended until recovery completes. Do not choose a different version mid-recovery.
110
+ - **Launcher unavailable:** run `node bin/aimship.mjs status --home <installation-home>` from the exact source/package installer. A missing launcher or damaged diagnostic module cannot diagnose itself. `status` reports facts without repairing them; only after inspecting the cause should you rerun the exact installer or reconcile retained files.
111
+ - **Unexpected version:** use the stable launcher for the installed active version. A CLI inside an older release can report a different tool version; reload the current binding before continuing. A source checkout is not automatically an installed release.
112
+ - **Package not found/authentication error:** confirm the exact package/version and public registry. Installation of a public release does not itself prove publish rights. Do not paste credentials into a bug report.
113
+ - **Skill absent or duplicated:** inspect the actual host selector and returned entry paths after reloading. A file check alone cannot diagnose host discovery; retain this as unverified until observed.
114
+ - **Directory denied:** authorize only the needed delivery/code roots in that host. Shared material storage does not share permissions. Report the exact denied operation rather than relocating user materials or changing security settings.
115
+ - **Different host/session:** explicitly resume the same absolute delivery directory and inspect state/current inputs. Do not copy materials, reset the goal or run two writers. See [the user guide](guide.md).
116
+
117
+ The initial data root can be selected with `--data-root` on installation. Existing configuration is retained on later installs; no language setting or second installation mode is introduced. Low-level state/version/recovery contracts are in [the CLI protocol](cli.md).
package/lib/cli.mjs ADDED
@@ -0,0 +1,159 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import { exists, fail, noSymlinks, readJSON } from './io.mjs';
5
+ import { install, installPaths, installationStatus, installationVersion, readConfig, uninstall } from './install.mjs';
6
+ import { SCHEMA_VERSION, STAGES, assertInstallationComplete, assertRules, checkReferences, initState, readState, recoverState, updateState, validateState } from './state.mjs';
7
+
8
+ const HELP = {
9
+ usage: 'node <aimship.mjs> <command> [delivery-directory] [options]',
10
+ commands: {
11
+ status: 'status [--home <dir>]. Read only Aimship-managed paths and fixed entries. Report registration, shared entry presence/integrity, active version, pending work and config problems. Host loading, login and access permissions remain unverified; no directories are created.',
12
+ bind: 'Resolve active installation once. Returns immutable release/core/stage/CLI paths plus configured dataRoot, configPath and customPath. Source mode returns the default data root with null config/custom paths. No directories are created. Optional --home is for isolated testing.',
13
+ init: 'init <dir> --input <json-file|-> --rules-version <version>. Create state once; input requires goal {summary, endpoint} and goalRef. Does not create templates.',
14
+ read: 'read <dir> [--rules-version <version>]. Read-only JSON access; unknown schemas remain readable.',
15
+ update: 'update <dir> --revision <N> --rules-version <version> --input <json-file|->. Recursively merge objects; arrays replace in full. Identity/history fields are managed. null is a literal value, not deletion.',
16
+ check: 'check <dir> [--rules-version <version>] [--all]. Check structure and current input references (or every registered reference). Does not prove semantics, anchor contents, remote access, or validation results.',
17
+ resume: 'resume <dir> [--rules-version <version>]. Read state and return fixed rule paths, required inputs, warnings, and continuation text; never execute work or rewrite state.',
18
+ checkpoint: 'checkpoint <dir> --revision <N> --rules-version <old-version> --input <json-file|->. Save only checkpoint {summary, ref?} and next when a version mismatch interrupts ongoing work. Cannot complete work.',
19
+ adopt: 'adopt <dir> --revision <N> --rules-version <active-version> --input <json-file|->. Input is {compatibilityRef}; records the assessed stable-point rule switch without changing business history.',
20
+ recover: 'recover <dir> --revision <backup-revision> --rules-version <version>. Restore state.previous.json only over missing/corrupt state; preserve corrupted bytes. Inspect actual artifacts afterward; nothing reruns.',
21
+ install: 'install --host <codex|cursor|claude-code> [--host ...] [--data-root <dir>] [--home <dir>]. Explicitly copy the whole release into .aimship. Repeated installs retain registered hosts. Installed tools default to their owning home; explicit --home overrides it for isolated testing. Data root is configured, never created.',
22
+ uninstall: 'uninstall [--host <host> ...] [--home <dir>]. --host disconnects selected hosts, retaining shared core even when no hosts remain. Omit --host for full product uninstall. Delivery data, config, custom and modified/unowned files are retained. Installed tools default to their owning home; explicit --home overrides it.',
23
+ help: 'help [command]. Every command supports --help. --version reports the executing tool version and installed active version separately. install is the only installation/update entry; update modifies delivery state.'
24
+ },
25
+ stateProtocol: {
26
+ schemaVersion: SCHEMA_VERSION,
27
+ stages: STAGES,
28
+ progress: ['planned', 'active', 'blocked', 'done'],
29
+ result: ['passed', 'failed', 'not-run', 'unknown', 'mixed'],
30
+ applicability: ['current', 'needs-review', 'unknown'],
31
+ outcome: { goal: ['in-progress', 'met', 'not-met', 'unknown'], acceptance: ['not-recorded', 'accepted', 'declined', 'unknown'] },
32
+ stageInstance: { id: 'stable-instance-id', stage: '<stage>', scope: 'batch or whole-delivery scope', progress: 'planned', inputRefs: [], outputRefs: [], resultRef: 'results.md#unique-historical-section (required when done)' },
33
+ references: 'delivery-relative/path#optional-fragment, repo:<registered-id>/<repo-relative-path>#fragment, or HTTP(S) URL. Local paths cannot escape their root. Repository registration: {id, path: absolute-path, role}.',
34
+ topLevel: {
35
+ managed: 'schemaVersion, revision, deliveryId, rulesVersion, createdAt, updatedAt, goalHistory, rulesHistory. Do not supply these in init/update.',
36
+ requiredInit: 'goal: {summary, endpoint}; goalRef: current requirements or Scope reference. Save the original request in that referenced document before init.',
37
+ stageRuns: 'Ordered array of stageInstance objects, default []. Every ID is unique; currentRun must match one of these IDs or be null. Stage types may repeat with distinct IDs.',
38
+ currentRun: 'Instance ID or null (default). Do not invent an ID without registering its instance in stageRuns.',
39
+ repositories: 'Array of {id, path: absolute-path, role, ...optional-version-facts}, default []. No repository is required for a design-only goal.',
40
+ openChanges: 'Array of {id, ref, status: open|closed}, default []; close records rather than deleting history.',
41
+ outcome: 'Defaults to {goal: in-progress, acceptance: not-recorded}. A met goal needs goalConclusionRef; recorded acceptance needs acceptanceRef. Optional handoffRef, mergeRef, deploymentRef retain independent facts.',
42
+ next: '{action, inputRefs?: []}; defaults to continuing the confirmed arrangement.',
43
+ cycle: 'Positive integer, default 1. A reopening increments by one, appends a closureRef and resets current acceptance.',
44
+ closureRefs: 'Historical closure references, default []. Preserve them across reopenings.'
45
+ },
46
+ minimalInit: {
47
+ goal: { summary: 'Repair the search empty-state message', endpoint: 'Implement, review, and verify the agreed repair' },
48
+ goalRef: 'requirements.md',
49
+ stageRuns: [{ id: 'diagnose-1', stage: 'diagnose', scope: 'Search empty-state repair', progress: 'planned', inputRefs: ['requirements.md'], outputRefs: [] }],
50
+ currentRun: 'diagnose-1',
51
+ next: { action: 'Reproduce and diagnose the reported message', inputRefs: ['requirements.md'] }
52
+ },
53
+ safety: 'Done is execution progress only. Results, applicability, goal attainment, acceptance, merge, and deployment are independent. Preserve old result references with priorResultRef/historyResultRefs. Goal revision needs a new goal.decisionRef; prior goals are retained automatically.'
54
+ }
55
+ };
56
+ function parse(argv) {
57
+ const positional = []; const options = {};
58
+ for (let i = 0; i < argv.length; i++) {
59
+ const item = argv[i];
60
+ if (!item.startsWith('--')) { positional.push(item); continue; }
61
+ const name = item.slice(2);
62
+ if (!['help', 'version', 'all', 'input', 'rules-version', 'revision', 'host', 'home', 'data-root'].includes(name)) fail('USAGE', `Unknown option: ${item}`, 'Run help for command usage.');
63
+ const value = ['help', 'version', 'all'].includes(name) ? true : argv[++i];
64
+ if (value === undefined || (typeof value === 'string' && value.startsWith('--'))) fail('USAGE', `Missing value for ${item}`, 'Provide the option value.');
65
+ if (name === 'host') (options.hosts ??= []).push(value);
66
+ else { if (Object.hasOwn(options, name)) fail('USAGE', `Repeated option: ${item}`, 'Provide one value; only --host is repeatable.'); options[name] = value; }
67
+ }
68
+ return { positional, options };
69
+ }
70
+ async function inputJSON(file) {
71
+ if (!file) fail('USAGE', '--input is required.', 'Use a JSON file or --input - to read stdin.');
72
+ if (file !== '-') return readJSON(path.resolve(file));
73
+ try { let raw = ''; for await (const chunk of process.stdin) raw += chunk; return JSON.parse(raw); } catch (e) { fail('INVALID_JSON', 'Cannot parse JSON from stdin.', 'Provide one valid JSON object.', { cause: e.message }); }
74
+ }
75
+ function revisionOf(value) {
76
+ if (!/^(0|[1-9][0-9]*)$/.test(value ?? '') || !Number.isSafeInteger(Number(value))) fail('USAGE', '--revision requires a nonnegative integer.', 'Use the exact revision returned by read (backup revision for recover).');
77
+ return Number(value);
78
+ }
79
+ function installedRoot(sourceRoot) { return path.basename(path.dirname(sourceRoot)) === 'releases' ? path.dirname(path.dirname(sourceRoot)) : null; }
80
+ async function runtimeFor(sourceRoot, options, explicitRoot) {
81
+ const pkg = await readJSON(path.join(sourceRoot, 'package.json'));
82
+ const root = explicitRoot ?? installedRoot(sourceRoot) ?? (options.home ? installPaths(options.home).root : null);
83
+ let activeVersion = pkg.version;
84
+ let installPending = null;
85
+ if (root) {
86
+ const manifest = await readJSON(path.join(root, 'install-manifest.json'));
87
+ if (manifest.schemaVersion !== 1 || manifest.product !== 'aimship') fail('INSTALL_FORMAT', 'Unknown installation manifest.', 'Use a compatible installer.');
88
+ installPending = manifest.pending ?? null;
89
+ if (await exists(path.join(root, 'active.json'))) {
90
+ const active = await readJSON(path.join(root, 'active.json'));
91
+ if (active.launcherProtocol !== 1 || typeof active.version !== 'string' || !/^[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.-]+)?$/.test(active.version)) fail('INSTALL_FORMAT', 'Active pointer has an unsupported protocol or version.', 'Reinstall the exact complete package.');
92
+ activeVersion = active.version;
93
+ } else if (!installPending) fail('NOT_INSTALLED', 'No active installation exists.', 'Install the exact package explicitly.');
94
+ }
95
+ return { version: pkg.version, activeVersion, installPending, releaseRoot: sourceRoot, installRoot: root, launcher: root ? path.join(root, 'bin', 'aimship.mjs') : path.join(sourceRoot, 'bin', 'aimship.mjs') };
96
+ }
97
+ async function binding(runtime) {
98
+ assertInstallationComplete(runtime);
99
+ const root = runtime.installRoot ? path.join(runtime.installRoot, 'releases', runtime.activeVersion) : runtime.releaseRoot;
100
+ await noSymlinks(root);
101
+ const pkg = await readJSON(path.join(root, 'package.json'));
102
+ if (pkg.version !== runtime.activeVersion) fail('RELEASE_CONFLICT', 'Active pointer and release package version differ.', 'Repair installation before continuing.');
103
+ const corePath = path.join(root, 'skills', 'aimship', 'references', 'core.md');
104
+ if (!await exists(corePath)) fail('INCOMPLETE_RELEASE', 'The bound core is missing.', 'Repair the exact installation before continuing.');
105
+ const configPath = runtime.installRoot ? path.join(runtime.installRoot, 'config.json') : null;
106
+ let dataRoot = path.join(os.homedir(), 'Aimship');
107
+ if (configPath) {
108
+ dataRoot = (await readConfig(configPath)).dataRoot;
109
+ }
110
+ return { version: pkg.version, schemaVersion: SCHEMA_VERSION, launcherProtocol: 1, releaseRoot: root, corePath, stageDirectory: path.join(root, 'skills', 'aimship', 'references', 'stages'), cliPath: path.join(root, 'bin', 'aimship.mjs'), launcherPath: runtime.launcher, nodePath: process.execPath, dataRoot, configPath, customPath: runtime.installRoot ? path.join(runtime.installRoot, 'custom') : null, configurationStatus: runtime.installRoot ? 'installed' : 'source-default' };
111
+ }
112
+ function allowed(command, options) {
113
+ const common = ['help', 'version'];
114
+ const perCommand = { status: ['home'], bind: ['home'], init: ['input', 'rules-version', 'home'], read: ['rules-version', 'home'], update: ['input', 'rules-version', 'revision', 'home'], check: ['all', 'rules-version', 'home'], resume: ['rules-version', 'home'], checkpoint: ['input', 'rules-version', 'revision', 'home'], adopt: ['input', 'rules-version', 'revision', 'home'], recover: ['rules-version', 'revision', 'home'], install: ['hosts', 'home', 'data-root'], uninstall: ['hosts', 'home'], help: [] };
115
+ for (const key of Object.keys(options)) if (![...common, ...(perCommand[command] ?? [])].includes(key)) fail('USAGE', `Option --${key} is not valid for ${command}.`, `Run help ${command}.`);
116
+ }
117
+ export async function run(argv, context) {
118
+ if (Number(process.versions.node.split('.')[0]) < 22) fail('NODE_VERSION', 'Aimship requires Node.js 22 or later.', 'Install a maintained Node LTS release and rerun this exact package.');
119
+ const { positional, options } = parse(argv); const command = positional[0] ?? 'help';
120
+ const pkg = await readJSON(path.join(context.sourceRoot, 'package.json'));
121
+ const owningInstallRoot = context.installRoot ?? installedRoot(context.sourceRoot);
122
+ const installHome = options.home ?? (owningInstallRoot ? path.dirname(owningInstallRoot) : undefined);
123
+ if (options.version) {
124
+ const installed = await installationVersion({ home: installHome });
125
+ return { command: 'version', data: { version: pkg.version, toolVersion: pkg.version, installedActiveVersion: installed.installedActiveVersion, installation: installed.installation, pendingVersion: installed.pending?.version ?? null, node: process.version, integrity: 'Use status for managed-file and config integrity; host loading, login and permissions are not verified.', issues: [installed.manifest.error, installed.active.error].filter(Boolean) } };
126
+ }
127
+ if (command === 'help' || options.help) {
128
+ const subject = command === 'help' ? positional[1] : command;
129
+ if (subject && !HELP.commands[subject]) fail('USAGE', `Unknown command: ${subject}`, 'Run help.');
130
+ return { command: 'help', data: subject ? { usage: HELP.commands[subject], stateProtocol: HELP.stateProtocol } : HELP };
131
+ }
132
+ if (!HELP.commands[command]) fail('USAGE', `Unknown command: ${command}`, 'Run help.');
133
+ allowed(command, options);
134
+ if (['install', 'uninstall', 'bind', 'status'].includes(command) ? positional.length !== 1 : positional.length !== 2) fail('USAGE', 'Unexpected or missing positional arguments.', `Run help ${command}.`);
135
+ if (command === 'status') return { command, data: { toolVersion: pkg.version, ...await installationStatus({ home: installHome }) } };
136
+ if (command === 'install') return { command, data: await install(context.sourceRoot, { home: installHome, hosts: options.hosts, dataRoot: options['data-root'] }) };
137
+ if (command === 'uninstall') return { command, data: await uninstall({ home: installHome, hosts: options.hosts }) };
138
+ const runtime = await runtimeFor(context.sourceRoot, options, context.installRoot);
139
+ if (command === 'bind') return { command, data: await binding(runtime) };
140
+ const directory = path.resolve(positional[1]); const rulesVersion = options['rules-version'];
141
+ if (command === 'init') return { command, data: await initState(directory, await inputJSON(options.input), rulesVersion, runtime) };
142
+ if (['update', 'checkpoint', 'adopt'].includes(command)) return { command, data: await updateState(directory, await inputJSON(options.input), revisionOf(options.revision), rulesVersion, runtime, command) };
143
+ if (command === 'recover') return { command, data: await recoverState(directory, revisionOf(options.revision), rulesVersion, runtime) };
144
+ const read = await readState(directory);
145
+ let mismatch = null;
146
+ if (!read.readOnly) { try { assertRules(read.state, rulesVersion, runtime); } catch (error) { mismatch = { code: error.code, ...error.details, recovery: error.recovery }; } }
147
+ if (command === 'read') return { command, data: { state: read.state, readOnly: read.readOnly, versionMismatch: mismatch, statePath: path.join(directory, 'state.json') } };
148
+ if (read.readOnly) return { command, data: { state: read.state, readOnly: true, canProceed: false, reason: 'Unknown schema: no structure checking, mutation, or automatic work is permitted.' } };
149
+ validateState(read.state); const references = await checkReferences(directory, read.state, options.all);
150
+ const missing = references.filter(x => !['exists', 'not-checked'].includes(x.status));
151
+ if (command === 'check') {
152
+ if (missing.length) fail('REFERENCE_CHECK', 'Required references are missing or inaccessible.', 'Repair the specific registered paths; do not scan the disk or overwrite source materials.', { references, versionMismatch: mismatch });
153
+ if (mismatch) fail('VERSION_MISMATCH', 'State is structurally valid, but rules are not bound consistently.', mismatch.recovery, mismatch);
154
+ return { command, data: { structure: 'valid', references, semantics: 'not-checked' } };
155
+ }
156
+ const currentRun = read.state.stageRuns.find(x => x.id === read.state.currentRun) ?? null;
157
+ const blocked = currentRun?.progress === 'blocked';
158
+ return { command, data: { state: read.state, binding: await binding(runtime), readOnly: false, canProceed: !mismatch && missing.length === 0 && !blocked, versionMismatch: mismatch, references, currentRun, continuation: `Use Aimship to continue ${directory}; first read state.json and the current inputs it references, then follow the confirmed arrangement. Inspect unfinished effects and related artifacts before repeating any action.`, warnings: ['Resume does not rerun work, tests, migrations, or other side effects.', 'Structural readiness does not establish authorization, business readiness, or goal completion.', ...(blocked ? [`Current run ${currentRun.id} is recorded as blocked. Resolve its documented blocker before progressing this work.`] : []), ...(missing.length ? ['Some required paths must be repaired.'] : []), ...(read.state.checkpoint ? ['Inspect the saved checkpoint and current artifacts.'] : [])] } };
159
+ }