@lsctech/polaris 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/README.md +87 -0
- package/dist/__mocks__/tool-server-linear.js +34 -0
- package/dist/cli/adopt-approve.js +60 -0
- package/dist/cli/adopt-cognition.js +199 -0
- package/dist/cli/adopt-instructions.js +143 -0
- package/dist/cli/adopt-rules.js +169 -0
- package/dist/cli/adopt-scan.js +25 -0
- package/dist/cli/adopt-smartdocs.js +192 -0
- package/dist/cli/adoption-inventory.js +350 -0
- package/dist/cli/adoption-plan.js +222 -0
- package/dist/cli/args.js +28 -0
- package/dist/cli/args.test.js +30 -0
- package/dist/cli/branding.js +41 -0
- package/dist/cli/graph.js +358 -0
- package/dist/cli/index.js +127 -0
- package/dist/cli/index.test.js +162 -0
- package/dist/cli/init-detect.js +61 -0
- package/dist/cli/init.js +492 -0
- package/dist/cli/init.test.js +259 -0
- package/dist/cli/librarian.js +32 -0
- package/dist/cli/medic.js +96 -0
- package/dist/cli/startup.test.js +23 -0
- package/dist/cli/tracker.js +130 -0
- package/dist/cli/tracker.test.js +126 -0
- package/dist/cli/version.js +10 -0
- package/dist/cli/version.test.js +11 -0
- package/dist/cli/worker.js +287 -0
- package/dist/cluster-state/fixtures/test-clusters.json +19 -0
- package/dist/cluster-state/store.js +302 -0
- package/dist/cluster-state/store.test.js +145 -0
- package/dist/cluster-state/types.js +2 -0
- package/dist/cognition/archive.js +158 -0
- package/dist/cognition/closeout-librarian-types.js +161 -0
- package/dist/cognition/cognition.test.js +623 -0
- package/dist/cognition/index.js +41 -0
- package/dist/cognition/librarian-dispatch.js +391 -0
- package/dist/cognition/librarian-dispatch.test.js +247 -0
- package/dist/cognition/librarian-packet.js +313 -0
- package/dist/cognition/librarian-types.js +57 -0
- package/dist/cognition/route-cognition-delta.js +350 -0
- package/dist/cognition/summary-delta.js +279 -0
- package/dist/cognition/validate.js +162 -0
- package/dist/config/defaults.js +98 -0
- package/dist/config/doctor.js +273 -0
- package/dist/config/index.js +14 -0
- package/dist/config/loader.js +58 -0
- package/dist/config/provider-detect.js +60 -0
- package/dist/config/provider-detect.test.js +119 -0
- package/dist/config/schema.js +2 -0
- package/dist/config/show.js +44 -0
- package/dist/config/show.test.js +42 -0
- package/dist/config/validator.js +675 -0
- package/dist/config/validator.test.js +77 -0
- package/dist/docs/canon-check.js +334 -0
- package/dist/docs/canon-check.test.js +207 -0
- package/dist/docs/doctrine.js +133 -0
- package/dist/docs/doctrine.test.js +139 -0
- package/dist/docs/index.js +168 -0
- package/dist/docs/index.test.js +83 -0
- package/dist/docs/ingest.js +295 -0
- package/dist/docs/ingest.test.js +71 -0
- package/dist/docs/migrate.js +165 -0
- package/dist/docs/migrate.test.js +136 -0
- package/dist/docs/seed-instructions.js +147 -0
- package/dist/docs/seed-instructions.test.js +137 -0
- package/dist/docs/validate-instructions.js +267 -0
- package/dist/docs/validate-instructions.test.js +238 -0
- package/dist/finalize/artifact-policy.js +214 -0
- package/dist/finalize/artifact-policy.test.js +84 -0
- package/dist/finalize/delivery-integrity.js +141 -0
- package/dist/finalize/finalize.test.js +210 -0
- package/dist/finalize/github.js +41 -0
- package/dist/finalize/index.js +474 -0
- package/dist/finalize/linear.js +213 -0
- package/dist/finalize/run-report.js +47 -0
- package/dist/finalize/steps/01-map-update.js +11 -0
- package/dist/finalize/steps/02-map-validate.js +11 -0
- package/dist/finalize/steps/03-schema-validate.js +11 -0
- package/dist/finalize/steps/04-run-checks.js +118 -0
- package/dist/finalize/steps/05-generate-report.js +14 -0
- package/dist/finalize/steps/06-commit.js +39 -0
- package/dist/finalize/steps/07-push.js +10 -0
- package/dist/finalize/steps/08-create-pr.js +9 -0
- package/dist/finalize/steps/09-update-state.js +9 -0
- package/dist/finalize/steps/10-append-jsonl.js +23 -0
- package/dist/finalize/steps/11-update-linear.js +22 -0
- package/dist/finalize/steps/12-archive.js +21 -0
- package/dist/graph/adapter/c/adapter.js +45 -0
- package/dist/graph/adapter/c/extract.js +135 -0
- package/dist/graph/adapter/c/index.js +10 -0
- package/dist/graph/adapter/c/runtime.js +67 -0
- package/dist/graph/adapter/cpp/adapter.js +45 -0
- package/dist/graph/adapter/cpp/extract.js +159 -0
- package/dist/graph/adapter/cpp/index.js +10 -0
- package/dist/graph/adapter/cpp/runtime.js +61 -0
- package/dist/graph/adapter/csharp/adapter.js +45 -0
- package/dist/graph/adapter/csharp/extract.js +127 -0
- package/dist/graph/adapter/csharp/index.js +10 -0
- package/dist/graph/adapter/csharp/runtime.js +61 -0
- package/dist/graph/adapter/dart/adapter.js +56 -0
- package/dist/graph/adapter/dart/extract.js +130 -0
- package/dist/graph/adapter/dart/index.js +10 -0
- package/dist/graph/adapter/dart/runtime.js +61 -0
- package/dist/graph/adapter/go/adapter.js +45 -0
- package/dist/graph/adapter/go/extract.js +112 -0
- package/dist/graph/adapter/go/index.js +10 -0
- package/dist/graph/adapter/go/runtime.js +58 -0
- package/dist/graph/adapter/kotlin-java/extract-common.js +83 -0
- package/dist/graph/adapter/kotlin-java/index.js +15 -0
- package/dist/graph/adapter/kotlin-java/java-adapter.js +45 -0
- package/dist/graph/adapter/kotlin-java/java-extract.js +42 -0
- package/dist/graph/adapter/kotlin-java/kotlin-adapter.js +48 -0
- package/dist/graph/adapter/kotlin-java/kotlin-extract.js +58 -0
- package/dist/graph/adapter/kotlin-java/runtime.js +75 -0
- package/dist/graph/adapter/python/adapter.js +45 -0
- package/dist/graph/adapter/python/extract.js +121 -0
- package/dist/graph/adapter/python/index.js +10 -0
- package/dist/graph/adapter/python/runtime.js +70 -0
- package/dist/graph/adapter/registry.js +82 -0
- package/dist/graph/adapter/rust/adapter.js +45 -0
- package/dist/graph/adapter/rust/extract.js +110 -0
- package/dist/graph/adapter/rust/index.js +10 -0
- package/dist/graph/adapter/rust/runtime.js +74 -0
- package/dist/graph/adapter/shell/adapter.js +34 -0
- package/dist/graph/adapter/shell/extract.js +98 -0
- package/dist/graph/adapter/shell/index.js +8 -0
- package/dist/graph/adapter/svelte/adapter.js +91 -0
- package/dist/graph/adapter/svelte/extract.js +41 -0
- package/dist/graph/adapter/svelte/index.js +8 -0
- package/dist/graph/adapter/swift/adapter.js +45 -0
- package/dist/graph/adapter/swift/extract.js +120 -0
- package/dist/graph/adapter/swift/index.js +10 -0
- package/dist/graph/adapter/swift/runtime.js +70 -0
- package/dist/graph/adapter/types.js +2 -0
- package/dist/graph/adapter/typescript-javascript/adapter.js +72 -0
- package/dist/graph/adapter/typescript-javascript/extract.js +115 -0
- package/dist/graph/adapter/typescript-javascript/index.js +10 -0
- package/dist/graph/adapter/typescript-javascript/runtime.js +97 -0
- package/dist/graph/capability/index.js +105 -0
- package/dist/graph/governance.js +104 -0
- package/dist/graph/parser/extract.js +5 -0
- package/dist/graph/parser/loader.js +2 -0
- package/dist/graph/parser/pipeline.js +194 -0
- package/dist/graph/query/index.js +277 -0
- package/dist/graph/query/types.js +5 -0
- package/dist/graph/resolver/build-edges.js +211 -0
- package/dist/graph/resolver/index.js +23 -0
- package/dist/graph/resolver/resolve-imports.js +168 -0
- package/dist/graph/store/adapter.js +132 -0
- package/dist/graph/store/queries.js +124 -0
- package/dist/graph/store/types.js +4 -0
- package/dist/ignore/defaults.js +41 -0
- package/dist/ignore/parser.js +39 -0
- package/dist/lint/map-reference-validator.js +72 -0
- package/dist/loop/abort.js +188 -0
- package/dist/loop/abort.test.js +151 -0
- package/dist/loop/adapters/__tests__/terminal-cli.test.js +92 -0
- package/dist/loop/adapters/agent-subtask.js +137 -0
- package/dist/loop/adapters/agent-subtask.test.js +151 -0
- package/dist/loop/adapters/cli-subtask-bridge.js +108 -0
- package/dist/loop/adapters/index.js +9 -0
- package/dist/loop/adapters/registry.js +17 -0
- package/dist/loop/adapters/terminal-cli.js +403 -0
- package/dist/loop/adapters/types.js +2 -0
- package/dist/loop/adapters/worker-instructions.js +85 -0
- package/dist/loop/body-parser.js +226 -0
- package/dist/loop/bootstrap-packet.js +97 -0
- package/dist/loop/bootstrap-packet.test.js +171 -0
- package/dist/loop/branch-governance.js +54 -0
- package/dist/loop/budget.js +63 -0
- package/dist/loop/budget.test.js +163 -0
- package/dist/loop/checkpoint.js +325 -0
- package/dist/loop/compact-return.js +59 -0
- package/dist/loop/continue.js +645 -0
- package/dist/loop/continue.test.js +337 -0
- package/dist/loop/dispatch-boundary.js +335 -0
- package/dist/loop/dispatch-state.js +223 -0
- package/dist/loop/dispatch-state.test.js +186 -0
- package/dist/loop/dispatch.js +1236 -0
- package/dist/loop/dispatch.test.js +499 -0
- package/dist/loop/ephemeral-smoke.test.js +160 -0
- package/dist/loop/evidence-backfill.js +216 -0
- package/dist/loop/execution-adapter.js +141 -0
- package/dist/loop/execution-adapter.test.js +66 -0
- package/dist/loop/finalize-evidence.js +169 -0
- package/dist/loop/git-custody.js +250 -0
- package/dist/loop/impl-packet-input.js +38 -0
- package/dist/loop/index.js +270 -0
- package/dist/loop/ledger.js +49 -0
- package/dist/loop/ledger.test.js +127 -0
- package/dist/loop/lifecycle-dispatch.js +348 -0
- package/dist/loop/lifecycle-dispatch.test.js +318 -0
- package/dist/loop/lifecycle.js +93 -0
- package/dist/loop/lifecycle.test.js +150 -0
- package/dist/loop/orphan-recovery.js +276 -0
- package/dist/loop/orphan-recovery.test.js +236 -0
- package/dist/loop/parent.js +1566 -0
- package/dist/loop/parent.test.js +738 -0
- package/dist/loop/provider-sealed-packet.test.js +164 -0
- package/dist/loop/resume.js +295 -0
- package/dist/loop/resume.test.js +172 -0
- package/dist/loop/run-bootstrap.js +234 -0
- package/dist/loop/run-preflight.js +203 -0
- package/dist/loop/status.js +665 -0
- package/dist/loop/status.test.js +235 -0
- package/dist/loop/wait.js +86 -0
- package/dist/loop/worker-packet.js +406 -0
- package/dist/loop/worker-packet.test.js +243 -0
- package/dist/loop/worker-prompt.js +260 -0
- package/dist/loop/worker-prompt.test.js +247 -0
- package/dist/loop/worker.js +498 -0
- package/dist/loop/worker.test.js +372 -0
- package/dist/map/atlas.js +77 -0
- package/dist/map/backfill.js +171 -0
- package/dist/map/backfill.test.js +120 -0
- package/dist/map/index.js +255 -0
- package/dist/map/inference.js +73 -0
- package/dist/map/inference.test.js +35 -0
- package/dist/map/query.js +157 -0
- package/dist/map/query.test.js +174 -0
- package/dist/map/update.js +355 -0
- package/dist/map/update.test.js +67 -0
- package/dist/map/validate.js +153 -0
- package/dist/map/validate.test.js +90 -0
- package/dist/map/welfare.js +86 -0
- package/dist/mcp/lib/invoke.js +35 -0
- package/dist/mcp/lib/redact.js +24 -0
- package/dist/mcp/lib/redact.test.js +112 -0
- package/dist/mcp/lib/root.js +41 -0
- package/dist/mcp/lib/root.test.js +114 -0
- package/dist/mcp/server.js +15 -0
- package/dist/mcp/tools/claim-child.js +119 -0
- package/dist/mcp/tools/claim-child.test.js +69 -0
- package/dist/mcp/tools/current-state.js +73 -0
- package/dist/mcp/tools/current-state.test.js +114 -0
- package/dist/mcp/tools/dispatch-result.js +186 -0
- package/dist/mcp/tools/dispatch-result.test.js +109 -0
- package/dist/mcp/tools/index.js +222 -0
- package/dist/mcp/tools/index.test.js +172 -0
- package/dist/mcp/tools/linear.js +74 -0
- package/dist/mcp/tools/loop-continue.js +159 -0
- package/dist/mcp/tools/loop-continue.test.js +186 -0
- package/dist/mcp/tools/loop-dry-run.js +15 -0
- package/dist/mcp/tools/status.js +65 -0
- package/dist/medic/chart-id.js +75 -0
- package/dist/medic/chart-schema.js +139 -0
- package/dist/runs/index.js +119 -0
- package/dist/runs/index.test.js +184 -0
- package/dist/runs/ledger-tail.js +15 -0
- package/dist/runs/list.js +29 -0
- package/dist/runs/reconcile.js +74 -0
- package/dist/runs/show.js +10 -0
- package/dist/runtime/audit/logger.js +15 -0
- package/dist/runtime/audit/reader.js +42 -0
- package/dist/runtime/checkpoint.js +89 -0
- package/dist/runtime/continuation/confirmed.js +225 -0
- package/dist/runtime/continuation/confirmed.test.js +421 -0
- package/dist/runtime/continuation/dry-run.js +74 -0
- package/dist/runtime/execution-window.js +106 -0
- package/dist/runtime/scheduling/child-selector.js +16 -0
- package/dist/runtime/state.js +43 -0
- package/dist/runtime/status.js +46 -0
- package/dist/runtime/verification/envelope.js +80 -0
- package/dist/runtime/verification/fingerprint.js +26 -0
- package/dist/skill-packet/cli.test.js +105 -0
- package/dist/skill-packet/generator.js +225 -0
- package/dist/skill-packet/generator.test.js +159 -0
- package/dist/skill-packet/index.js +49 -0
- package/dist/skill-packet/types.js +2 -0
- package/dist/smartdocs-engine/audit.js +194 -0
- package/dist/smartdocs-engine/audit.test.js +122 -0
- package/dist/smartdocs-engine/canon-check.js +340 -0
- package/dist/smartdocs-engine/canon-check.test.js +234 -0
- package/dist/smartdocs-engine/doctrine.js +633 -0
- package/dist/smartdocs-engine/doctrine.test.js +374 -0
- package/dist/smartdocs-engine/index.js +456 -0
- package/dist/smartdocs-engine/index.test.js +82 -0
- package/dist/smartdocs-engine/ingest.js +495 -0
- package/dist/smartdocs-engine/ingest.test.js +163 -0
- package/dist/smartdocs-engine/migrate.js +217 -0
- package/dist/smartdocs-engine/migrate.test.js +200 -0
- package/dist/smartdocs-engine/seed-instructions.js +335 -0
- package/dist/smartdocs-engine/seed-instructions.test.js +275 -0
- package/dist/smartdocs-engine/smartdoc-ignore.js +258 -0
- package/dist/smartdocs-engine/smartdoc-ignore.test.js +146 -0
- package/dist/smartdocs-engine/validate-instructions.js +366 -0
- package/dist/smartdocs-engine/validate-instructions.test.js +283 -0
- package/dist/tracker/adapters/github/index.js +305 -0
- package/dist/tracker/adapters/jira/index.js +293 -0
- package/dist/tracker/adapters/linear/http-errors.test.js +51 -0
- package/dist/tracker/adapters/linear/index.js +621 -0
- package/dist/tracker/adapters/linear/index.test.js +238 -0
- package/dist/tracker/adapters/linear.js +50 -0
- package/dist/tracker/adapters/local/index.js +10 -0
- package/dist/tracker/adapters/local-file/index.js +159 -0
- package/dist/tracker/adapters/mcp-bridge.js +158 -0
- package/dist/tracker/adapters/mcp-bridge.test.js +12 -0
- package/dist/tracker/adapters/spec/index.js +146 -0
- package/dist/tracker/capabilities.js +2 -0
- package/dist/tracker/index.js +49 -0
- package/dist/tracker/lifecycle-policy.js +139 -0
- package/dist/tracker/lifecycle-transition.js +164 -0
- package/dist/tracker/local-graph.js +98 -0
- package/dist/tracker/migration.js +56 -0
- package/dist/tracker/schema.js +86 -0
- package/dist/tracker/sync/index.js +404 -0
- package/dist/tracker/sync/queue-store.js +95 -0
- package/dist/tracker/sync/queue-store.test.js +104 -0
- package/dist/tracker/types.js +11 -0
- package/dist/types/linear.js +3 -0
- package/dist/types/result-packet.js +12 -0
- package/dist/types/runtime-state.js +2 -0
- package/dist/types/work-contract.js +2 -0
- package/dist/utils/monotonic-timestamp.js +13 -0
- package/dist/utils/monotonic-timestamp.test.js +19 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/ItIsYeBananaduck/Polaris/main/branding/assets/polaris-logo.png" alt="Polaris CLO" width="200" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Polaris CLO</h1>
|
|
6
|
+
<p align="center"><strong>Command Line Orchestrator</strong></p>
|
|
7
|
+
<p align="center"><em>Navigate. Align. Orchestrate.</em></p>
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Polaris is a taskchain orchestration framework for governed AI agent workflows. It dispatches, tracks, and finalizes implementation work across AI providers — keeping every run bounded, auditable, and connected to your issue tracker.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @lsctech/polaris
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Requires **Node.js 22+**.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Initialize Polaris in your repository
|
|
25
|
+
polaris init --adopt
|
|
26
|
+
|
|
27
|
+
# Check configuration
|
|
28
|
+
polaris doctor
|
|
29
|
+
|
|
30
|
+
# Start a governed run
|
|
31
|
+
polaris run POL-123
|
|
32
|
+
|
|
33
|
+
# Check status
|
|
34
|
+
polaris status
|
|
35
|
+
|
|
36
|
+
# Finalize completed work
|
|
37
|
+
polaris finalize
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Configuration
|
|
41
|
+
|
|
42
|
+
Polaris is configured via `polaris.config.json` at the repository root.
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"version": "1.0",
|
|
47
|
+
"repo": {
|
|
48
|
+
"name": "my-project",
|
|
49
|
+
"sourceRoots": ["src"]
|
|
50
|
+
},
|
|
51
|
+
"tracker": {
|
|
52
|
+
"adapter": "github",
|
|
53
|
+
"github": {
|
|
54
|
+
"enabled": true,
|
|
55
|
+
"owner": "my-org",
|
|
56
|
+
"repo": "my-project",
|
|
57
|
+
"labelPrefix": "status:"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Tracker Adapters
|
|
64
|
+
|
|
65
|
+
| Adapter | Description |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `github` | GitHub Issues — label-based lifecycle states, PAT auth |
|
|
68
|
+
| `linear` | Linear — team/project scoped, API key auth |
|
|
69
|
+
| `jira` | Jira Cloud — REST API v3, Basic auth |
|
|
70
|
+
| `local` | Local file graph only, no external sync |
|
|
71
|
+
|
|
72
|
+
Set credentials via environment variables:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
export GITHUB_TOKEN=ghp_...
|
|
76
|
+
export LINEAR_API_KEY=lin_api_...
|
|
77
|
+
export JIRA_API_TOKEN=your_token
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Documentation
|
|
81
|
+
|
|
82
|
+
- **[Setup Guide](docs/SETUP.md)** — Installation, tracker configuration, environment variables
|
|
83
|
+
- **[Usage Guide](docs/USAGE.md)** — Daily workflow, commands, configuration reference, troubleshooting
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT — © LSC Technologies
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mcp_linear_save_issue = exports.mcp_linear_get_issue = exports.mcp_linear_get_issue_status = exports.mcp_linear_list_issues = exports.mcp_linear_list_projects = exports.mcp_linear_list_teams = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Stub for @tool-server/linear used in test environments.
|
|
6
|
+
* The real module is an MCP runtime tool server — not an npm package.
|
|
7
|
+
* Tests that need specific behaviour should override with vi.mock().
|
|
8
|
+
*/
|
|
9
|
+
const mcp_linear_list_teams = async (..._args) => [];
|
|
10
|
+
exports.mcp_linear_list_teams = mcp_linear_list_teams;
|
|
11
|
+
const mcp_linear_list_projects = async (..._args) => [];
|
|
12
|
+
exports.mcp_linear_list_projects = mcp_linear_list_projects;
|
|
13
|
+
const mcp_linear_list_issues = async (..._args) => [];
|
|
14
|
+
exports.mcp_linear_list_issues = mcp_linear_list_issues;
|
|
15
|
+
const mcp_linear_get_issue_status = async (..._args) => [];
|
|
16
|
+
exports.mcp_linear_get_issue_status = mcp_linear_get_issue_status;
|
|
17
|
+
const mcp_linear_get_issue = async (..._args) => ({
|
|
18
|
+
id: 'stub-issue',
|
|
19
|
+
title: 'Stub Issue',
|
|
20
|
+
description: '',
|
|
21
|
+
state: { id: 'stub-state', name: 'Todo', type: 'unstarted' },
|
|
22
|
+
assignee: null,
|
|
23
|
+
createdAt: '2023-01-01T00:00:00.000Z',
|
|
24
|
+
});
|
|
25
|
+
exports.mcp_linear_get_issue = mcp_linear_get_issue;
|
|
26
|
+
const mcp_linear_save_issue = async (..._args) => ({
|
|
27
|
+
id: 'stub-issue',
|
|
28
|
+
title: 'Stub Issue',
|
|
29
|
+
description: '',
|
|
30
|
+
state: { id: 'stub-state', name: 'Todo', type: 'unstarted' },
|
|
31
|
+
assignee: null,
|
|
32
|
+
createdAt: '2023-01-01T00:00:00.000Z',
|
|
33
|
+
});
|
|
34
|
+
exports.mcp_linear_save_issue = mcp_linear_save_issue;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.persistApprovedAdoptionPlan = persistApprovedAdoptionPlan;
|
|
4
|
+
exports.logAdoptionApprovalTelemetry = logAdoptionApprovalTelemetry;
|
|
5
|
+
exports.promptApproval = promptApproval;
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const promises_1 = require("node:readline/promises");
|
|
9
|
+
const adoption_plan_js_1 = require("./adoption-plan.js");
|
|
10
|
+
function adoptionPlanJsonPath(repoRoot) {
|
|
11
|
+
return (0, node_path_1.join)(repoRoot, ".polaris", "adoption-plan.json");
|
|
12
|
+
}
|
|
13
|
+
function adoptionPlanMarkdownPath(repoRoot) {
|
|
14
|
+
return (0, node_path_1.join)(repoRoot, ".polaris", "adoption-plan.md");
|
|
15
|
+
}
|
|
16
|
+
function adoptionTelemetryPath(repoRoot) {
|
|
17
|
+
return (0, node_path_1.join)(repoRoot, ".polaris", "adoption-telemetry.jsonl");
|
|
18
|
+
}
|
|
19
|
+
function writeTelemetryEvent(repoRoot, event) {
|
|
20
|
+
const telemetryPath = adoptionTelemetryPath(repoRoot);
|
|
21
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(telemetryPath), { recursive: true });
|
|
22
|
+
(0, node_fs_1.appendFileSync)(telemetryPath, `${JSON.stringify(event)}\n`, "utf-8");
|
|
23
|
+
}
|
|
24
|
+
function persistApprovedAdoptionPlan(plan, repoRoot = process.cwd(), now = new Date()) {
|
|
25
|
+
plan.approved = true;
|
|
26
|
+
plan.approved_at = plan.approved_at ?? now.toISOString();
|
|
27
|
+
const jsonPath = adoptionPlanJsonPath(repoRoot);
|
|
28
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(jsonPath), { recursive: true });
|
|
29
|
+
(0, node_fs_1.writeFileSync)(jsonPath, `${JSON.stringify(plan, null, 2)}\n`, "utf-8");
|
|
30
|
+
}
|
|
31
|
+
function logAdoptionApprovalTelemetry(repoRoot, event) {
|
|
32
|
+
writeTelemetryEvent(repoRoot, event);
|
|
33
|
+
}
|
|
34
|
+
async function promptApproval(plan, options = {}) {
|
|
35
|
+
const repoRoot = options.repoRoot ?? process.cwd();
|
|
36
|
+
const stdout = options.stdout ?? process.stdout;
|
|
37
|
+
const stdin = options.stdin ?? process.stdin;
|
|
38
|
+
const markdownPath = adoptionPlanMarkdownPath(repoRoot);
|
|
39
|
+
const markdown = (0, node_fs_1.existsSync)(markdownPath)
|
|
40
|
+
? (0, node_fs_1.readFileSync)(markdownPath, "utf-8")
|
|
41
|
+
: (0, adoption_plan_js_1.renderAdoptionPlanMarkdown)(plan);
|
|
42
|
+
stdout.write(`${markdown.replace(/\s*$/, "")}\n\n`);
|
|
43
|
+
const rl = (0, promises_1.createInterface)({
|
|
44
|
+
input: stdin,
|
|
45
|
+
output: stdout,
|
|
46
|
+
});
|
|
47
|
+
let response = "";
|
|
48
|
+
try {
|
|
49
|
+
response = await rl.question("Proceed with adoption? [y/N] ");
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
rl.close();
|
|
53
|
+
}
|
|
54
|
+
if (response.trim().toLowerCase() === "y") {
|
|
55
|
+
persistApprovedAdoptionPlan(plan, repoRoot, options.now ?? new Date());
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
stdout.write("Adoption aborted: explicit approval required.\n");
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateFolderCognition = generateFolderCognition;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const EXCLUDED_SEGMENTS = new Set(["node_modules", "dist", ".git"]);
|
|
7
|
+
const SOURCE_FILE_EXTENSIONS = new Set([
|
|
8
|
+
".ts",
|
|
9
|
+
".tsx",
|
|
10
|
+
".js",
|
|
11
|
+
".jsx",
|
|
12
|
+
".mjs",
|
|
13
|
+
".cjs",
|
|
14
|
+
".py",
|
|
15
|
+
".go",
|
|
16
|
+
".rs",
|
|
17
|
+
".java",
|
|
18
|
+
".kt",
|
|
19
|
+
".swift",
|
|
20
|
+
".rb",
|
|
21
|
+
".php",
|
|
22
|
+
".c",
|
|
23
|
+
".cc",
|
|
24
|
+
".cpp",
|
|
25
|
+
".h",
|
|
26
|
+
".hpp",
|
|
27
|
+
".cs",
|
|
28
|
+
".scala",
|
|
29
|
+
".sh",
|
|
30
|
+
]);
|
|
31
|
+
function toPosix(path) {
|
|
32
|
+
return path.replaceAll("\\", "/");
|
|
33
|
+
}
|
|
34
|
+
function normalizeFolder(path) {
|
|
35
|
+
const normalized = toPosix(path).replace(/^\.\//, "").replace(/\/+$/, "");
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
function toRootPrefix(path) {
|
|
39
|
+
const normalized = normalizeFolder(path);
|
|
40
|
+
return normalized.length > 0 ? `${normalized}/` : normalized;
|
|
41
|
+
}
|
|
42
|
+
function isUnderRoot(path, roots) {
|
|
43
|
+
const normalizedPath = normalizeFolder(path);
|
|
44
|
+
return roots
|
|
45
|
+
.map(toRootPrefix)
|
|
46
|
+
.filter((root) => root.length > 0)
|
|
47
|
+
.some((root) => normalizedPath === root.slice(0, -1) || normalizedPath.startsWith(root));
|
|
48
|
+
}
|
|
49
|
+
function hasExcludedSegments(path) {
|
|
50
|
+
const normalized = normalizeFolder(path);
|
|
51
|
+
if (normalized.startsWith(".polaris/runs/") || normalized === ".polaris/runs") {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (normalized.startsWith(".polaris/bootstrap/") || normalized === ".polaris/bootstrap") {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return normalized.split("/").some((segment) => EXCLUDED_SEGMENTS.has(segment));
|
|
58
|
+
}
|
|
59
|
+
function isFolderEligible(path, inventory) {
|
|
60
|
+
const normalized = normalizeFolder(path);
|
|
61
|
+
if (normalized.length === 0)
|
|
62
|
+
return false;
|
|
63
|
+
if (normalized === "smartdocs" || normalized.startsWith("smartdocs/"))
|
|
64
|
+
return false;
|
|
65
|
+
if (isUnderRoot(normalized, inventory.generated_roots))
|
|
66
|
+
return false;
|
|
67
|
+
if (isUnderRoot(normalized, inventory.cache_roots))
|
|
68
|
+
return false;
|
|
69
|
+
if (isUnderRoot(normalized, inventory.fixture_roots))
|
|
70
|
+
return false;
|
|
71
|
+
if (hasExcludedSegments(normalized))
|
|
72
|
+
return false;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
function countSourceFiles(dirPath, count = 0) {
|
|
76
|
+
if (count >= 3) {
|
|
77
|
+
return count;
|
|
78
|
+
}
|
|
79
|
+
let entries = [];
|
|
80
|
+
try {
|
|
81
|
+
entries = (0, node_fs_1.readdirSync)(dirPath, { withFileTypes: true });
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return count;
|
|
85
|
+
}
|
|
86
|
+
let sourceCount = count;
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
const entryPath = (0, node_path_1.join)(dirPath, entry.name);
|
|
89
|
+
const normalized = toPosix(entryPath);
|
|
90
|
+
if (entry.isDirectory()) {
|
|
91
|
+
if (entry.name === "node_modules" ||
|
|
92
|
+
entry.name === "dist" ||
|
|
93
|
+
entry.name === ".git" ||
|
|
94
|
+
normalized.includes("/.polaris/runs/") ||
|
|
95
|
+
normalized.includes("/.polaris/bootstrap/")) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
sourceCount = countSourceFiles(entryPath, sourceCount);
|
|
99
|
+
if (sourceCount >= 3) {
|
|
100
|
+
return sourceCount;
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (entry.isFile()) {
|
|
105
|
+
const dotIndex = entry.name.lastIndexOf(".");
|
|
106
|
+
if (dotIndex > 0 && SOURCE_FILE_EXTENSIONS.has(entry.name.slice(dotIndex).toLowerCase())) {
|
|
107
|
+
sourceCount += 1;
|
|
108
|
+
}
|
|
109
|
+
if (sourceCount >= 3) {
|
|
110
|
+
return sourceCount;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return sourceCount;
|
|
115
|
+
}
|
|
116
|
+
function buildPolarisDraft(folder) {
|
|
117
|
+
const folderName = (0, node_path_1.basename)(folder);
|
|
118
|
+
return [
|
|
119
|
+
"<!-- polaris:draft -->",
|
|
120
|
+
`# ${folderName}`,
|
|
121
|
+
"",
|
|
122
|
+
"> Polaris draft — review and remove the `<!-- polaris:draft -->` marker to promote.",
|
|
123
|
+
"",
|
|
124
|
+
"## Purpose",
|
|
125
|
+
"",
|
|
126
|
+
"<!-- One paragraph describing what this folder does. -->",
|
|
127
|
+
"",
|
|
128
|
+
"**Domain:** unknown",
|
|
129
|
+
`**Route:** ${folder}`,
|
|
130
|
+
"**Taskchain:** unknown",
|
|
131
|
+
"",
|
|
132
|
+
"## What belongs here",
|
|
133
|
+
"",
|
|
134
|
+
"<!-- Describe the assets and responsibilities expected in this folder. -->",
|
|
135
|
+
"",
|
|
136
|
+
"## What does not belong here",
|
|
137
|
+
"",
|
|
138
|
+
"<!-- Describe content that should be routed elsewhere. -->",
|
|
139
|
+
"",
|
|
140
|
+
].join("\n");
|
|
141
|
+
}
|
|
142
|
+
function buildSummaryDraft(folder) {
|
|
143
|
+
const folderName = (0, node_path_1.basename)(folder);
|
|
144
|
+
return [
|
|
145
|
+
"<!-- polaris:draft -->",
|
|
146
|
+
`# Summary — ${folderName}`,
|
|
147
|
+
"",
|
|
148
|
+
"> Polaris draft — review and remove the `<!-- polaris:draft -->` marker to promote.",
|
|
149
|
+
"",
|
|
150
|
+
`- Route: \`${folder}\``,
|
|
151
|
+
"- Canon status: draft",
|
|
152
|
+
"- Linked doctrine: `POLARIS.md`",
|
|
153
|
+
"",
|
|
154
|
+
"## Notes",
|
|
155
|
+
"",
|
|
156
|
+
"<!-- Add concise context, references, and ownership details for this folder. -->",
|
|
157
|
+
"",
|
|
158
|
+
].join("\n");
|
|
159
|
+
}
|
|
160
|
+
function generateFolderCognition(plan, inventory) {
|
|
161
|
+
if (plan.dry_run) {
|
|
162
|
+
return Promise.resolve();
|
|
163
|
+
}
|
|
164
|
+
const repoRoot = (0, node_path_1.resolve)(process.cwd());
|
|
165
|
+
const folders = Array.from(new Set(inventory.likely_canonical_folders.map(normalizeFolder)));
|
|
166
|
+
for (const folder of folders) {
|
|
167
|
+
if (!isFolderEligible(folder, inventory)) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const absoluteFolder = (0, node_path_1.join)(repoRoot, folder);
|
|
171
|
+
if (!(0, node_fs_1.existsSync)(absoluteFolder)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
let folderStat;
|
|
175
|
+
try {
|
|
176
|
+
folderStat = (0, node_fs_1.statSync)(absoluteFolder);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (!folderStat.isDirectory()) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (countSourceFiles(absoluteFolder) < 3) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const polarisPath = (0, node_path_1.join)(absoluteFolder, "POLARIS.md");
|
|
188
|
+
if ((0, node_fs_1.existsSync)(polarisPath)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const summaryPath = (0, node_path_1.join)(absoluteFolder, "SUMMARY.md");
|
|
192
|
+
(0, node_fs_1.mkdirSync)(absoluteFolder, { recursive: true });
|
|
193
|
+
(0, node_fs_1.writeFileSync)(polarisPath, buildPolarisDraft(folder), "utf-8");
|
|
194
|
+
if (!(0, node_fs_1.existsSync)(summaryPath)) {
|
|
195
|
+
(0, node_fs_1.writeFileSync)(summaryPath, buildSummaryDraft(folder), "utf-8");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return Promise.resolve();
|
|
199
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleInstructionFiles = handleInstructionFiles;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const DELEGATION_MARKERS = ["<!-- polaris:delegate", "POLARIS.md", "Polaris runtime"];
|
|
7
|
+
function isSupportedInstructionPath(path) {
|
|
8
|
+
return (path === "CLAUDE.md" ||
|
|
9
|
+
path === "AGENTS.md" ||
|
|
10
|
+
path === ".github/copilot-instructions.md" ||
|
|
11
|
+
path === ".cursorrules" ||
|
|
12
|
+
path === ".aider.conf.yml" ||
|
|
13
|
+
path === "AIDER.md" ||
|
|
14
|
+
/^\.cursor\/rules\/.*\.md$/i.test(path) ||
|
|
15
|
+
/(^|\/)GEMINI\.md$/i.test(path));
|
|
16
|
+
}
|
|
17
|
+
function hasDoctrine(repoRoot) {
|
|
18
|
+
return ((0, node_fs_1.existsSync)((0, node_path_1.join)(repoRoot, "POLARIS_RULES.md")) ||
|
|
19
|
+
(0, node_fs_1.existsSync)((0, node_path_1.join)(repoRoot, "POLARIS.md")) ||
|
|
20
|
+
(0, node_fs_1.existsSync)((0, node_path_1.join)(repoRoot, "smartdocs", "doctrine", "active")));
|
|
21
|
+
}
|
|
22
|
+
function hasDelegationMarker(content) {
|
|
23
|
+
return DELEGATION_MARKERS.some((marker) => content.includes(marker));
|
|
24
|
+
}
|
|
25
|
+
function classifyInstruction(content, doctrineExists, _repoHints) {
|
|
26
|
+
if (hasDelegationMarker(content)) {
|
|
27
|
+
return { decision: "preserve", reason: "already contains Polaris delegation markers" };
|
|
28
|
+
}
|
|
29
|
+
if (!doctrineExists) {
|
|
30
|
+
return { decision: "preserve", reason: "no Polaris doctrine exists yet" };
|
|
31
|
+
}
|
|
32
|
+
// When POLARIS_RULES.md exists, all agent files become pointer-only.
|
|
33
|
+
// Substantive content is migrated to smartdocs/raw/migrated-instructions.
|
|
34
|
+
return { decision: "thin-adapter", reason: "POLARIS_RULES.md exists — convert to pointer" };
|
|
35
|
+
}
|
|
36
|
+
function toBackupFileName(sourcePath) {
|
|
37
|
+
const cleaned = sourcePath.replace(/^\.+\//, "").replaceAll("/", "__");
|
|
38
|
+
return cleaned.length > 0 ? cleaned : (0, node_path_1.basename)(sourcePath);
|
|
39
|
+
}
|
|
40
|
+
function reserveBackupPath(baseDirectory, sourcePath) {
|
|
41
|
+
const baseName = toBackupFileName(sourcePath);
|
|
42
|
+
let candidate = (0, node_path_1.join)(baseDirectory, baseName);
|
|
43
|
+
if (!(0, node_fs_1.existsSync)(candidate)) {
|
|
44
|
+
return candidate;
|
|
45
|
+
}
|
|
46
|
+
const dotIndex = baseName.lastIndexOf(".");
|
|
47
|
+
const stem = dotIndex > 0 ? baseName.slice(0, dotIndex) : baseName;
|
|
48
|
+
const ext = dotIndex > 0 ? baseName.slice(dotIndex) : "";
|
|
49
|
+
let index = 1;
|
|
50
|
+
while ((0, node_fs_1.existsSync)(candidate)) {
|
|
51
|
+
candidate = (0, node_path_1.join)(baseDirectory, `${stem}-${index}${ext}`);
|
|
52
|
+
index += 1;
|
|
53
|
+
}
|
|
54
|
+
return candidate;
|
|
55
|
+
}
|
|
56
|
+
function preserveOriginalContent(repoRoot, sourcePath, content) {
|
|
57
|
+
const archiveRoot = (0, node_path_1.join)(repoRoot, "smartdocs", "raw", "migrated-instructions");
|
|
58
|
+
(0, node_fs_1.mkdirSync)(archiveRoot, { recursive: true });
|
|
59
|
+
const backupPath = reserveBackupPath(archiveRoot, sourcePath);
|
|
60
|
+
(0, node_fs_1.writeFileSync)(backupPath, content, "utf-8");
|
|
61
|
+
const roundTrip = (0, node_fs_1.readFileSync)(backupPath, "utf-8");
|
|
62
|
+
if (roundTrip !== content) {
|
|
63
|
+
throw new Error(`Failed to preserve original instruction content for ${sourcePath}`);
|
|
64
|
+
}
|
|
65
|
+
return backupPath;
|
|
66
|
+
}
|
|
67
|
+
function buildThinAdapter(_sourcePath) {
|
|
68
|
+
return [
|
|
69
|
+
"# Polaris Managed Repository",
|
|
70
|
+
"",
|
|
71
|
+
"Read [POLARIS_RULES.md](POLARIS_RULES.md) before doing any work in this repository.",
|
|
72
|
+
"",
|
|
73
|
+
].join("\n");
|
|
74
|
+
}
|
|
75
|
+
function appendInstructionProvenance(repoRoot, records) {
|
|
76
|
+
if (records.length === 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const provenancePath = (0, node_path_1.join)(repoRoot, ".polaris", "adoption-provenance.json");
|
|
80
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(repoRoot, ".polaris"), { recursive: true });
|
|
81
|
+
let existing = {};
|
|
82
|
+
if ((0, node_fs_1.existsSync)(provenancePath)) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(provenancePath, "utf-8"));
|
|
85
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
86
|
+
existing = parsed;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
existing = {};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const prior = Array.isArray(existing.instruction_file_actions)
|
|
94
|
+
? existing.instruction_file_actions
|
|
95
|
+
: [];
|
|
96
|
+
const updated = {
|
|
97
|
+
...existing,
|
|
98
|
+
updated_at: new Date().toISOString(),
|
|
99
|
+
instruction_file_actions: [...prior, ...records],
|
|
100
|
+
};
|
|
101
|
+
(0, node_fs_1.writeFileSync)(provenancePath, `${JSON.stringify(updated, null, 2)}\n`, "utf-8");
|
|
102
|
+
}
|
|
103
|
+
function handleInstructionFiles(plan, inventory) {
|
|
104
|
+
if (plan.dry_run) {
|
|
105
|
+
return Promise.resolve();
|
|
106
|
+
}
|
|
107
|
+
const repoRoot = (0, node_path_1.resolve)(process.cwd());
|
|
108
|
+
const doctrineExists = hasDoctrine(repoRoot);
|
|
109
|
+
const now = new Date().toISOString();
|
|
110
|
+
const candidates = Array.from(new Set(inventory.agent_instruction_files
|
|
111
|
+
.map((entry) => entry.path)
|
|
112
|
+
.filter((path) => isSupportedInstructionPath(path))));
|
|
113
|
+
const provenance = [];
|
|
114
|
+
for (const relativePath of candidates) {
|
|
115
|
+
const absolutePath = (0, node_path_1.join)(repoRoot, relativePath);
|
|
116
|
+
if (!(0, node_fs_1.existsSync)(absolutePath)) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const originalContent = (0, node_fs_1.readFileSync)(absolutePath, "utf-8");
|
|
120
|
+
const { decision, reason } = classifyInstruction(originalContent, doctrineExists, []);
|
|
121
|
+
if (decision === "preserve") {
|
|
122
|
+
provenance.push({
|
|
123
|
+
source_path: relativePath,
|
|
124
|
+
decision,
|
|
125
|
+
reason,
|
|
126
|
+
backup_path: null,
|
|
127
|
+
timestamp: now,
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const backupAbsolutePath = preserveOriginalContent(repoRoot, relativePath, originalContent);
|
|
132
|
+
(0, node_fs_1.writeFileSync)(absolutePath, buildThinAdapter(relativePath), "utf-8");
|
|
133
|
+
provenance.push({
|
|
134
|
+
source_path: relativePath,
|
|
135
|
+
decision,
|
|
136
|
+
reason,
|
|
137
|
+
backup_path: backupAbsolutePath.slice(repoRoot.length + 1).replaceAll("\\", "/"),
|
|
138
|
+
timestamp: now,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
appendInstructionProvenance(repoRoot, provenance);
|
|
142
|
+
return Promise.resolve();
|
|
143
|
+
}
|